-Add support for reading octal and hexadecimal numbers from JSON

-Fix serializing indexed properties
-Fix serializing JRaw properties
This commit is contained in:
JamesNK
2010-01-08 08:12:40 +00:00
parent 324c4e4bc5
commit 75649bfa00
14 changed files with 236 additions and 78 deletions
+12 -5
View File
@@ -20,7 +20,7 @@
equivalent and back again.</p> equivalent and back again.</p>
<p>For simple scenarios the <a href="./html/Overload_Newtonsoft_Json_JsonConvert_SerializeObject.htm">SerializeObject</a> and <a href="./html/Overload_Newtonsoft_Json_JsonConvert_DeserializeObject.htm">DeserializeObject</a> methods on <a href="./html/T_Newtonsoft_Json_JsonConvert.htm">JsonConvert</a> provide an easy to use wrapper over <p>For simple scenarios where you want to convert to and from a JSON string the <a href="./html/Overload_Newtonsoft_Json_JsonConvert_SerializeObject.htm">SerializeObject</a> and <a href="./html/Overload_Newtonsoft_Json_JsonConvert_DeserializeObject.htm">DeserializeObject</a> methods on <a href="./html/T_Newtonsoft_Json_JsonConvert.htm">JsonConvert</a> provide an easy to use wrapper over
JsonSerializer.</p> JsonSerializer.</p>
@@ -55,8 +55,11 @@
<p>For more control over how an object is serialized the JsonSerializer can be used <p>For more control over how an object is serialized the JsonSerializer can be used
directly. The JsonSerializer has a number of properties to control its behaviour directly. The JsonSerializer is able to read and write JSON text directly to
when serializing and deserializing.</p> a stream via <a href="./html/T_Newtonsoft_Json_JsonTextReader.htm">JsonTextReader</a> and <a href="./html/T_Newtonsoft_Json_JsonTextWriter.htm">JsonTextWriter</a>. Other kinds of JsonWriters can
also be used such as <a href="./html/T_Newtonsoft_Json_Linq_JTokenReader.htm">JTokenReader</a>/<a href="./html/T_Newtonsoft_Json_Linq_JTokenWriter.htm">JTokenWriter</a> to
convert your object to and
from LINQ to JSON objects or <a href="./html/T_Newtonsoft_Json_Bson_BsonReader.htm">BsonReader</a>/<a href="./html/T_Newtonsoft_Json_Bson_BsonWriter.htm">BsonWriter</a> to convert to and from BSON.</p>
<div class="overflowpanel"> <div class="code"> <div class="overflowpanel"> <div class="code">
@@ -77,6 +80,9 @@
</div> </div>
</div></div> </div></div>
<p>JsonSerializer has a number of properties on it to customize how it serializes JSON.
These can also be used with the methods on JsonConvert via the
JsonSerializerSettings overloads.</p>
<h5>ReferenceLoopHandling</h5> <h5>ReferenceLoopHandling</h5>
<p>Controls how circular referencing objects are serialized. Error, ignore or <p>Controls how circular referencing objects are serialized. Error, ignore or
@@ -114,11 +120,12 @@ the member's DefaultValueAttribute. Include or ignore.</p>
<h5>XmlNodeConverter</h5> <h5>XmlNodeConverter</h5>
<p>Converts an XmlNode to and from JSON. Note that to convert a JSON object it must have only a single property <p>Converts an XmlNode to and from JSON. Note that to convert a JSON object it must have only a single property
or you must define a root node name to be inserted when using this converter. This is required because properties are converted into nodes and or you must define a root node name to be inserted when using this converter. This is required because properties are converted into nodes and
well formed XML can only have one root node.</p> well formed XML can only have one root node. XmlNodeConverter has an option to
insert a root node for you.</p>
<h5>BinaryConverter</h5> <h5>BinaryConverter</h5>
<p>Converts binary data like a byte array or SqlBinary object to JSON. The binary <p>Converts binary data like the SqlBinary object to JSON. The binary
data is written as a string in JSON and is encoded in Base64.</p> data is written as a string in JSON and is encoded in Base64.</p>
<h5>CustomCreationConverter</h5> <h5>CustomCreationConverter</h5>
@@ -620,5 +620,32 @@ bye", reader.Value);
string text = Encoding.UTF8.GetString(data, 0, data.Length); string text = Encoding.UTF8.GetString(data, 0, data.Length);
Assert.AreEqual("Hello world.", text); Assert.AreEqual("Hello world.", text);
} }
[Test]
public void ReadOctalNumber()
{
StringReader s = new StringReader(@"[0372, 0xFA, 0XFA]");
JsonTextReader jsonReader = new JsonTextReader(s);
Assert.IsTrue(jsonReader.Read());
Assert.AreEqual(JsonToken.StartArray, jsonReader.TokenType);
Assert.IsTrue(jsonReader.Read());
Assert.AreEqual(JsonToken.Integer, jsonReader.TokenType);
Assert.AreEqual(250, jsonReader.Value);
Assert.IsTrue(jsonReader.Read());
Assert.AreEqual(JsonToken.Integer, jsonReader.TokenType);
Assert.AreEqual(250, jsonReader.Value);
Assert.IsTrue(jsonReader.Read());
Assert.AreEqual(JsonToken.Integer, jsonReader.TokenType);
Assert.AreEqual(250, jsonReader.Value);
Assert.IsTrue(jsonReader.Read());
Assert.AreEqual(JsonToken.EndArray, jsonReader.TokenType);
Assert.IsFalse(jsonReader.Read());
}
} }
} }
File diff suppressed because one or more lines are too long
@@ -2123,5 +2123,114 @@ keyword such as type of business.""
Assert.AreEqual(1, d.List.Count); Assert.AreEqual(1, d.List.Count);
Assert.AreEqual("existing", d.List[0]); Assert.AreEqual("existing", d.List[0]);
} }
public interface IKeyValueId
{
int Id { get; set; }
string Key { get; set; }
string Value { get; set; }
}
public class KeyValueId : IKeyValueId
{
public int Id { get; set; }
public string Key { get; set; }
public string Value { get; set; }
}
public class ThisGenericTest<T> where T : IKeyValueId
{
private Dictionary<string, T> _dict1 = new Dictionary<string, T>();
public string MyProperty { get; set; }
public void Add(T item)
{
this._dict1.Add(item.Key, item);
}
public T this[string key]
{
get { return this._dict1[key]; }
set { this._dict1[key] = value; }
}
public T this[int id]
{
get { return this._dict1.Values.FirstOrDefault(x => x.Id == id); }
set
{
var item = this[id];
if (item == null)
this.Add(value);
else
this._dict1[item.Key] = value;
}
}
public string ToJson()
{
return JsonConvert.SerializeObject(this, Formatting.Indented);
}
public T[] TheItems
{
get { return this._dict1.Values.ToArray<T>(); }
set
{
foreach (var item in value)
this.Add(item);
}
}
}
[Test]
public void IgnoreIndexedProperties()
{
ThisGenericTest<KeyValueId> g = new ThisGenericTest<KeyValueId>();
g.Add(new KeyValueId { Id = 1, Key = "key1", Value = "value1" });
g.Add(new KeyValueId { Id = 2, Key = "key2", Value = "value2" });
g.MyProperty = "some value";
string json = g.ToJson();
Assert.AreEqual(@"{
""MyProperty"": ""some value"",
""TheItems"": [
{
""Id"": 1,
""Key"": ""key1"",
""Value"": ""value1""
},
{
""Id"": 2,
""Key"": ""key2"",
""Value"": ""value2""
}
]
}", json);
ThisGenericTest<KeyValueId> gen = JsonConvert.DeserializeObject<ThisGenericTest<KeyValueId>>(json);
Assert.AreEqual("some value", gen.MyProperty);
}
public class JRawValueTestObject
{
public JRaw Value { get; set; }
}
[Test]
public void JRawValue()
{
JRawValueTestObject deserialized = JsonConvert.DeserializeObject<JRawValueTestObject>("{value:3}");
Assert.AreEqual("3", deserialized.Value.ToString());
deserialized = JsonConvert.DeserializeObject<JRawValueTestObject>("{value:'3'}");
Assert.AreEqual(@"""3""", deserialized.Value.ToString());
}
} }
} }
@@ -573,11 +573,6 @@ namespace Newtonsoft.Json.Converters
{ {
return c.OfType<XmlAttribute>().Where(a => a.NamespaceURI != JsonNamespaceUri); return c.OfType<XmlAttribute>().Where(a => a.NamespaceURI != JsonNamespaceUri);
} }
private IEnumerable<XmlNode> ValueNodes(XmlNodeList c)
{
return c.OfType<XmlNode>().Where(n => n.NamespaceURI != JsonNamespaceUri);
}
#endregion #endregion
/// <summary> /// <summary>
+13 -30
View File
@@ -41,54 +41,39 @@ namespace Newtonsoft.Json
/// <summary> /// <summary>
/// Represents JavaScript's boolean value true as a string. This field is read-only. /// Represents JavaScript's boolean value true as a string. This field is read-only.
/// </summary> /// </summary>
public static readonly string True; public static readonly string True = "true";
/// <summary> /// <summary>
/// Represents JavaScript's boolean value false as a string. This field is read-only. /// Represents JavaScript's boolean value false as a string. This field is read-only.
/// </summary> /// </summary>
public static readonly string False; public static readonly string False = "false";
/// <summary> /// <summary>
/// Represents JavaScript's null as a string. This field is read-only. /// Represents JavaScript's null as a string. This field is read-only.
/// </summary> /// </summary>
public static readonly string Null; public static readonly string Null = "null";
/// <summary> /// <summary>
/// Represents JavaScript's undefined as a string. This field is read-only. /// Represents JavaScript's undefined as a string. This field is read-only.
/// </summary> /// </summary>
public static readonly string Undefined; public static readonly string Undefined = "undefined";
/// <summary> /// <summary>
/// Represents JavaScript's positive infinity as a string. This field is read-only. /// Represents JavaScript's positive infinity as a string. This field is read-only.
/// </summary> /// </summary>
public static readonly string PositiveInfinity; public static readonly string PositiveInfinity = "Infinity";
/// <summary> /// <summary>
/// Represents JavaScript's negative infinity as a string. This field is read-only. /// Represents JavaScript's negative infinity as a string. This field is read-only.
/// </summary> /// </summary>
public static readonly string NegativeInfinity; public static readonly string NegativeInfinity = "-Infinity";
/// <summary> /// <summary>
/// Represents JavaScript's NaN as a string. This field is read-only. /// Represents JavaScript's NaN as a string. This field is read-only.
/// </summary> /// </summary>
public static readonly string NaN; public static readonly string NaN = "NaN";
internal static long InitialJavaScriptDateTicks; internal static readonly long InitialJavaScriptDateTicks = 621355968000000000;
internal static DateTime MinimumJavaScriptDate;
static JsonConvert()
{
True = "true";
False = "false";
Null = "null";
Undefined = "undefined";
PositiveInfinity = "Infinity";
NegativeInfinity = "-Infinity";
NaN = "NaN";
InitialJavaScriptDateTicks = (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).Ticks;
MinimumJavaScriptDate = new DateTime(100, 1, 1);
}
/// <summary> /// <summary>
/// Converts the <see cref="DateTime"/> to its JSON string representation. /// Converts the <see cref="DateTime"/> to its JSON string representation.
@@ -409,10 +394,10 @@ namespace Newtonsoft.Json
if (value == null) if (value == null)
return Null; return Null;
if (value is IConvertible) IConvertible convertible = value as IConvertible;
{
IConvertible convertible = (IConvertible)value;
if (convertible != null)
{
switch (convertible.GetTypeCode()) switch (convertible.GetTypeCode())
{ {
case TypeCode.String: case TypeCode.String:
@@ -502,12 +487,10 @@ namespace Newtonsoft.Json
if (value == null) if (value == null)
return true; return true;
if (value is IConvertible) IConvertible convertible = value as IConvertible;
{
IConvertible convertible = (IConvertible)value;
if (convertible != null)
return IsJsonPrimitiveTypeCode(convertible.GetTypeCode()); return IsJsonPrimitiveTypeCode(convertible.GetTypeCode());
}
#if !PocketPC && !NET20 #if !PocketPC && !NET20
if (value is DateTimeOffset) if (value is DateTimeOffset)
@@ -11,7 +11,7 @@ namespace Newtonsoft.Json
/// Instructs the <see cref="JsonSerializer"/> to use the specified <see cref="JsonConverter"/> when serializing the member or class. /// Instructs the <see cref="JsonSerializer"/> to use the specified <see cref="JsonConverter"/> when serializing the member or class.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class, AllowMultiple = false)]
public class JsonConverterAttribute : Attribute public sealed class JsonConverterAttribute : Attribute
{ {
private readonly Type _converterType; private readonly Type _converterType;
-3
View File
@@ -111,7 +111,6 @@ namespace Newtonsoft.Json
protected State CurrentState protected State CurrentState
{ {
get { return _currentState; } get { return _currentState; }
private set { _currentState = value; }
} }
private int _top; private int _top;
@@ -172,10 +171,8 @@ namespace Newtonsoft.Json
/// </summary> /// </summary>
protected JsonReader() protected JsonReader()
{ {
//_testBuffer = new StringBuilder();
_currentState = State.Start; _currentState = State.Start;
_stack = new List<JTokenType>(); _stack = new List<JTokenType>();
_top = 0;
Push(JTokenType.None); Push(JTokenType.None);
} }
+13 -5
View File
@@ -663,6 +663,7 @@ namespace Newtonsoft.Json
private void ParseNumber(char firstChar) private void ParseNumber(char firstChar)
{ {
char currentChar = firstChar; char currentChar = firstChar;
bool nonBase10 = (firstChar == '0');
// parse until seperator character or end // parse until seperator character or end
bool end = false; bool end = false;
@@ -684,16 +685,23 @@ namespace Newtonsoft.Json
object numberValue; object numberValue;
JsonToken numberType; JsonToken numberType;
if (number.IndexOf(".", StringComparison.OrdinalIgnoreCase) == -1 if (number.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1
&& number.IndexOf("e", StringComparison.OrdinalIgnoreCase) == -1) || number.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1)
{ {
numberValue = Convert.ToInt64(number, CultureInfo.InvariantCulture); numberValue = Convert.ToDouble(number, CultureInfo.InvariantCulture);
numberType = JsonToken.Float;
}
else if (nonBase10)
{
numberValue = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
? Convert.ToInt64(number, 16)
: Convert.ToInt64(number, 8);
numberType = JsonToken.Integer; numberType = JsonToken.Integer;
} }
else else
{ {
numberValue = Convert.ToDouble(number, CultureInfo.InvariantCulture); numberValue = Convert.ToInt64(number, CultureInfo.InvariantCulture);
numberType = JsonToken.Float; numberType = JsonToken.Integer;
} }
_buffer.Position = 0; _buffer.Position = 0;
-5
View File
@@ -291,12 +291,7 @@ namespace Newtonsoft.Json
if (value == null) if (value == null)
WriteValueInternal(JsonConvert.Null, JsonToken.Null); WriteValueInternal(JsonConvert.Null, JsonToken.Null);
else else
{
JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, true); JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, true);
//_writer.Write(JavaScriptUtils.EscapeJavaScriptString(value, _quoteChar, false));
//WriteValueInternal(, JsonToken.String);
//JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, true);
}
} }
/// <summary> /// <summary>
+26
View File
@@ -61,5 +61,31 @@ namespace Newtonsoft.Json.Linq
{ {
get { return new JEnumerable<JToken>(Extensions.Values<T, JToken>(_enumerable, key)); } get { return new JEnumerable<JToken>(Extensions.Values<T, JToken>(_enumerable, key)); }
} }
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
if (obj is JEnumerable<T>)
return _enumerable.Equals(((JEnumerable<T>)obj)._enumerable);
return false;
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode()
{
return _enumerable.GetHashCode();
}
} }
} }
@@ -79,21 +79,14 @@ namespace Newtonsoft.Json.Schema
private JsonSchemaResolver _resolver; private JsonSchemaResolver _resolver;
private IList<TypeSchema> _stack = new List<TypeSchema>(); private IList<TypeSchema> _stack = new List<TypeSchema>();
private JsonSchema _currentSchema; private JsonSchema _currentSchema;
private Type _currentType;
private JsonSchema CurrentSchema private JsonSchema CurrentSchema
{ {
get { return _currentSchema; } get { return _currentSchema; }
} }
private Type CurrentType
{
get { return _currentType; }
}
private void Push(TypeSchema typeSchema) private void Push(TypeSchema typeSchema)
{ {
_currentType = typeSchema.Type;
_currentSchema = typeSchema.Schema; _currentSchema = typeSchema.Schema;
_stack.Add(typeSchema); _stack.Add(typeSchema);
_resolver.LoadedSchemas.Add(typeSchema.Schema); _resolver.LoadedSchemas.Add(typeSchema.Schema);
@@ -106,12 +99,10 @@ namespace Newtonsoft.Json.Schema
TypeSchema newValue = _stack.LastOrDefault(); TypeSchema newValue = _stack.LastOrDefault();
if (newValue != null) if (newValue != null)
{ {
_currentType = newValue.Type;
_currentSchema = newValue.Schema; _currentSchema = newValue.Schema;
} }
else else
{ {
_currentType = null;
_currentSchema = null; _currentSchema = null;
} }
@@ -97,18 +97,22 @@ namespace Newtonsoft.Json.Serialization
DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(objectType); DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(objectType);
#endif #endif
List<MemberInfo> defaultMembers = ReflectionUtils.GetFieldsAndProperties(objectType, DefaultMembersSearchFlags); List<MemberInfo> defaultMembers = ReflectionUtils.GetFieldsAndProperties(objectType, DefaultMembersSearchFlags)
List<MemberInfo> allMembers = ReflectionUtils.GetFieldsAndProperties(objectType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); .Where(m => !ReflectionUtils.IsIndexedProperty(m)).ToList();
List<MemberInfo> allMembers = ReflectionUtils.GetFieldsAndProperties(objectType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)
.Where(m => !ReflectionUtils.IsIndexedProperty(m)).ToList();
List<MemberInfo> serializableMembers = new List<MemberInfo>(); List<MemberInfo> serializableMembers = new List<MemberInfo>();
foreach (MemberInfo member in allMembers) foreach (MemberInfo member in allMembers)
{ {
if (defaultMembers.Contains(member)) if (defaultMembers.Contains(member))
{ {
// add all members that are found by default member search
serializableMembers.Add(member); serializableMembers.Add(member);
} }
else else
{ {
// add members that are explicitly marked with JsonProperty/DataMember attribute
if (JsonTypeReflector.GetAttribute<JsonPropertyAttribute>(member) != null) if (JsonTypeReflector.GetAttribute<JsonPropertyAttribute>(member) != null)
serializableMembers.Add(member); serializableMembers.Add(member);
#if !PocketPC && !NET20 #if !PocketPC && !NET20
@@ -323,6 +327,11 @@ namespace Newtonsoft.Json.Serialization
{ {
return CreateObjectContract(objectType); return CreateObjectContract(objectType);
} }
if (objectType.IsSubclassOf(typeof(JToken)))
{
return CreateLinqContract(objectType);
}
if (CollectionUtils.IsDictionaryType(objectType)) if (CollectionUtils.IsDictionaryType(objectType))
{ {
return CreateDictionaryContract(objectType); return CreateDictionaryContract(objectType);
@@ -331,10 +340,6 @@ namespace Newtonsoft.Json.Serialization
{ {
return CreateArrayContract(objectType); return CreateArrayContract(objectType);
} }
if (objectType.IsSubclassOf(typeof(JToken)))
{
return CreateLinqContract(objectType);
}
return CreateObjectContract(objectType); return CreateObjectContract(objectType);
} }
@@ -550,9 +550,15 @@ namespace Newtonsoft.Json.Utilities
foreach (var groupedMember in groupedMembers) foreach (var groupedMember in groupedMembers)
{ {
if (groupedMember.Count == 1) if (groupedMember.Count == 1)
{
distinctMembers.Add(groupedMember.Members.First()); distinctMembers.Add(groupedMember.Members.First());
}
else else
distinctMembers.Add(groupedMember.Members.Where(m => !IsOverridenGenericMember(m, bindingAttr)).First()); {
var members = groupedMember.Members.Where(m => !IsOverridenGenericMember(m, bindingAttr) || m.Name == "Item");
distinctMembers.AddRange(members);
}
} }
return distinctMembers; return distinctMembers;