diff --git a/Doc/SerializationGuide.html b/Doc/SerializationGuide.html index 5f9f28b5..f83860a0 100644 --- a/Doc/SerializationGuide.html +++ b/Doc/SerializationGuide.html @@ -211,12 +211,12 @@

Objects

- .NET types that don’t fall into any other category listed below (i.e. aren’t lists, + .NET types that don't fall into any other category listed below (i.e. aren't lists, dictionaries, dynamic, implement ISerializable, etc) are serialized as JSON objects.

By default types are serialized in opt-out mode. What that means is all public fields and properties with getters are automatically serialized to JSON and members that - shouldn’t be serialized are opted-out using the JsonIgnoreAttribute. To serialize + shouldn't be serialized are opted-out using the JsonIgnoreAttribute. To serialize private members the JsonPropertyAttribute can be placed on private fields and properties or the DefaultMembersSearchFlags can be changed on DefaultContractResolver to change how members are serialized on all types.

@@ -251,7 +251,7 @@

.NET dictionaries (types that inherit from IDictionary) are converted to JSON objects. Note that only the dictionary name/values will be written to the JSON object when - serializing and properties on the JSON object will be added to the dictionary’s + serializing and properties on the JSON object will be added to the dictionary's name/values when deserializing. Additional members on the .NET dictionary are ignored during serialization.

@@ -263,40 +263,54 @@

Untyped Objects

- .NET properties on a class that don’t specify a type (i.e. they are just object) + .NET properties on a class that don't specify a type (i.e. they are just object) are serialized as usual. When untyped properties are deserialized the serializer has no way to know what type to create (unless type name handling is enabled and the JSON contains the type names).

For these untyped properties the Json.NET serializer will read the JSON into LINQ - to JSON objects and set them to the property. + to JSON objects and set them to the property. JObject will be created for JSON objects, + JArray will be created for JSON arrays and JValue for primitive JSON values.

Dynamic

- .NET dynamic objects (introduced in .NET 4) are serialized as JSON objects. A property - is written for every member name returned by IDynamicMetaObjectProvider.GetDynamicMemberNames.

-

- When deserializing the serializer first attempts to set JSON property values on - a normal .NET member with the matching name. If no .NET member is found with the - property name then the serializer will call SetMember on the dynamic object.

-

- Because a dynamic property on a class doesn’t specify a type, Json.NET has no way - to know what type to create for it when deserializing (unless type name handling - is enabled and the JSON contains the type name). LINQ to JSON objects will be created - for the JSON. + There are two different usages of dynamic (introduced in .NET 4) in .NET. The first + are .NET properties with a type of dynamic. Dynamic proeprties behave like properties + declared as object, any value can be assigned to it, but the difference being that + properties and methods can be called on a dynamic property without casting. In Json.NET + dynamic properties are serialized and deserialized exactly the same as untyped objects: + because dynamic isn't an actual type Json.NET falls back to deserializing the JSON + as LINQ to JSON objects.

+

+ The second usage of dynamic in .NET are classes that implement IDynamicMetaObjectProvider. This interface lets the implementor + create dynamic objects that intercept the property and method calls on an object + and use them. ExpandoObject is a good example of a dynamic object. +

+

+ Dynamic objects are serialized as JSON objects. A property is written for every + member name returned by DynamicMetaObject.GetDynamicMemberNames.

+

+ When deserializing dynamic objects the serializer first attempts to set JSON property + values on a normal .NET member with the matching name. If no .NET member is found + with the property name then the serializer will call SetMember on the dynamic object. + Because there is no type information for dynamic members on a dynamic object the + values assigned to them will be LINQ to JSON objects

ISerializable

Types that implement ISerializable are serialized as JSON objects. When serializing only the values returned from ISerializable.GetObjectData are used; members on the type are ignored. When deserializing the constructor with a SerializationInfo and - StreamingContext is called, passing the JSON object’s values.

+ StreamingContext is called, passing the JSON object's values.

- In situations where this behaviour is not wanted the JsonObjectAttribute can be - placed on a .NET type that implements ISerializable to force it to be serialized - as a normal JSON object. + In situations where this behavior is not wanted the JsonObjectAttribute can be placed + on a .NET type that implements ISerializable to force it to be serialized as a normal + JSON object.

LINQ to JSON

@@ -313,7 +327,7 @@ all other tests.

JsonConverters can be defined and specified in a number of places: in an attribute - on a member, in an attribute on a class and added to the JsonSerializer’s converters + on a member, in an attribute on a class and added to the JsonSerializer's converters collection. The priority of which JsonConverter is used is the JsonConverter defined by attribute on a member then the JsonConverter defined by an attribute on a class and finally any converters passed to the JsonSerializer.

diff --git a/Doc/SerializationSettings.html b/Doc/SerializationSettings.html index 0de5e248..21a51c27 100644 --- a/Doc/SerializationSettings.html +++ b/Doc/SerializationSettings.html @@ -178,7 +178,7 @@ DefaultValueHandling controls how Json.NET uses default values set using the .NE
By default Json.NET will write a field/property value - to JSON when serializing if the value is the same as the field/property’s + to JSON when serializing if the value is the same as the field/property's default value. The Json.NET deserializer will continue setting a field/property if the JSON value is the same as the default value.
@@ -192,8 +192,8 @@ DefaultValueHandling controls how Json.NET uses default values set using the .NE
Json.NET will skip writing a field/property value to - JSON if the value is the same as the field/property’s default value. The - Json.NET deserializer will skip setting a .NET object’s field/property if the + JSON if the value is the same as the field/property's default value. The + Json.NET deserializer will skip setting a .NET object's field/property if the JSON value is the same as the default value.

@@ -315,7 +315,7 @@ DefaultValueHandling controls how Json.NET uses default values set using the .NE Json.NET will check whether an object/collection matches its declared property and writes the type name if they do not match, e.g. a property with a type of Mammal has a derived instance of Dog assigned. Auto will - ensure that type information isn’t lost when serializing/deserializing + ensure that type information isn't lost when serializing/deserializing automatically without having to write type names for every object.
@@ -405,7 +405,7 @@ DefaultValueHandling controls how Json.NET uses default values set using the .NE
By default Json.NET will first look for a constructor marked with the JsonConstructorAttribute, then look for a public default - constructor (a constructor that doesn’t take any arguments) and finally check if + constructor (a constructor that doesn't take any arguments) and finally check if the class has a single public constructor with arguments. If the class has multiple public constructors with arguments an error will be thrown. This can be fixed by marking one of the constructors with the JsonConstructorAttribute. diff --git a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs index 731e3443..587ffdb8 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs @@ -1591,6 +1591,12 @@ Parameter name: arrayIndex")] Assert.AreEqual(false, prop4.ShouldSerializeValue(o)); } #endif + [Test] + public void ParseEmptyObjectWithComment() + { + JObject o = JObject.Parse("{ /* A Comment */ }"); + Assert.AreEqual(0, o.Count); + } [Test] public void FromObjectTimeSpan() diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index eb1028ff..4c00c76b 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -41,5 +41,5 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.2.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.2.14003")] +[assembly: AssemblyFileVersion("4.0.2.14201")] #endif diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index c446773b..0110be74 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -3426,6 +3426,7 @@ keyword such as type of business."" public int Age { get; set; } public int Weight { get; set; } public int Height { get; set; } + public int FavoriteNumber { get; set; } // dummy. should never be used because it isn't of type bool [JsonIgnore] @@ -3444,6 +3445,14 @@ keyword such as type of business."" [JsonIgnore] [System.Xml.Serialization.XmlIgnoreAttribute] public bool HeightSpecified; + + [JsonIgnore] + + public bool FavoriteNumberSpecified + { + // get only example + get { return FavoriteNumber != 0; } + } } [Test] @@ -3465,18 +3474,21 @@ keyword such as type of business."" Assert.IsFalse(deserialized.NameSpecified); Assert.IsFalse(deserialized.WeightSpecified); Assert.IsFalse(deserialized.HeightSpecified); + Assert.IsFalse(deserialized.FavoriteNumberSpecified); Assert.AreEqual(27, deserialized.Age); c.NameSpecified = true; c.WeightSpecified = true; c.HeightSpecified = true; + c.FavoriteNumber = 23; json = JsonConvert.SerializeObject(c, Formatting.Indented); Assert.AreEqual(@"{ ""Name"": ""James"", ""Age"": 27, ""Weight"": 0, - ""Height"": 0 + ""Height"": 0, + ""FavoriteNumber"": 23 }", json); deserialized = JsonConvert.DeserializeObject(json); @@ -3484,7 +3496,9 @@ keyword such as type of business."" Assert.IsTrue(deserialized.NameSpecified); Assert.IsTrue(deserialized.WeightSpecified); Assert.IsTrue(deserialized.HeightSpecified); + Assert.IsTrue(deserialized.FavoriteNumberSpecified); Assert.AreEqual(27, deserialized.Age); + Assert.AreEqual(23, deserialized.FavoriteNumber); } // [Test] @@ -4844,6 +4858,31 @@ keyword such as type of business."" Assert.AreEqual(c1.NullableTimeSpan, c2.NullableTimeSpan); Assert.AreEqual(c1.Uri, c2.Uri); } + + [Test] + public void NullableValueGenericDictionary() + { + IDictionary v1 = new Dictionary + { + { "First", 1 }, + { "Second", null }, + { "Third", 3 } + }; + + string json = JsonConvert.SerializeObject(v1, Formatting.Indented); + + Assert.AreEqual(@"{ + ""First"": 1, + ""Second"": null, + ""Third"": 3 +}", json); + + IDictionary v2 = JsonConvert.DeserializeObject>(json); + Assert.AreEqual(3, v2.Count); + Assert.AreEqual(1, v2["First"]); + Assert.AreEqual(null, v2["Second"]); + Assert.AreEqual(3, v2["Third"]); + } } public class UriGuidTimeSpanTestClass diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 121d36f1..fab78d5d 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -142,6 +142,15 @@ namespace Newtonsoft.Json.Linq return (t != null && ContentsEqual(t)); } + internal override void InsertItem(int index, JToken item) + { + // don't add comments to JObject, no name to reference comment by + if (item != null && item.Type == JTokenType.Comment) + return; + + base.InsertItem(index, item); + } + internal override void ValidateToken(JToken o, JToken existing) { ValidationUtils.ArgumentNotNull(o, "o"); @@ -480,16 +489,6 @@ namespace Newtonsoft.Json.Linq } } - /// - /// Gets the number of elements contained in the . - /// - /// - /// The number of elements contained in the . - public int Count - { - get { return ChildrenTokens.Count; } - } - bool ICollection>.IsReadOnly { get { return false; } diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 819cd7ca..da88140e 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -110,7 +110,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.2.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.2.14003")] +[assembly: AssemblyFileVersion("4.0.2.14201")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 887bf4a3..516ad93f 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -78,7 +78,11 @@ namespace Newtonsoft.Json.Serialization /// public class DefaultContractResolver : IContractResolver { - internal static readonly IContractResolver Instance = new DefaultContractResolver(true); + private static readonly IContractResolver _instance = new DefaultContractResolver(true); + internal static IContractResolver Instance + { + get { return _instance; } + } private static readonly IList BuiltInConverters = new List { #if !PocketPC && !SILVERLIGHT && !NET20 @@ -792,7 +796,7 @@ namespace Newtonsoft.Json.Serialization property.Writable = ReflectionUtils.CanSetMemberValue(member, allowNonPublicAccess, hasExplicitAttribute); property.ShouldSerialize = CreateShouldSerializeTest(member); - SetIsSpecifiedActions(property, member); + SetIsSpecifiedActions(property, member, allowNonPublicAccess); return property; } @@ -898,7 +902,7 @@ namespace Newtonsoft.Json.Serialization return o => (bool)shouldSerializeCall(o); } - private void SetIsSpecifiedActions(JsonProperty property, MemberInfo member) + private void SetIsSpecifiedActions(JsonProperty property, MemberInfo member, bool allowNonPublicAccess) { MemberInfo specifiedMember = member.DeclaringType.GetProperty(member.Name + JsonTypeReflector.SpecifiedPostfix); if (specifiedMember == null) @@ -912,7 +916,9 @@ namespace Newtonsoft.Json.Serialization Func specifiedPropertyGet = JsonTypeReflector.ReflectionDelegateFactory.CreateGet(specifiedMember); property.GetIsSpecified = o => (bool)specifiedPropertyGet(o); - property.SetIsSpecified = JsonTypeReflector.ReflectionDelegateFactory.CreateSet(specifiedMember); + + if (ReflectionUtils.CanSetMemberValue(specifiedMember, allowNonPublicAccess, false)) + property.SetIsSpecified = JsonTypeReflector.ReflectionDelegateFactory.CreateSet(specifiedMember); } /// diff --git a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs index 80c284cd..c69fa858 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs @@ -45,6 +45,7 @@ namespace Newtonsoft.Json.Serialization internal Type DictionaryKeyType { get; private set; } internal Type DictionaryValueType { get; private set; } + private readonly bool _isDictionaryValueTypeNullableType; private readonly Type _genericCollectionDefinitionType; private Type _genericWrapperType; private MethodCall _genericWrapperCreator; @@ -71,6 +72,9 @@ namespace Newtonsoft.Json.Serialization DictionaryKeyType = keyType; DictionaryValueType = valueType; + if (DictionaryValueType != null) + _isDictionaryValueTypeNullableType = ReflectionUtils.IsNullableType(DictionaryValueType); + if (IsTypeGenericDictionaryInterface(UnderlyingType)) { CreatedType = ReflectionUtils.MakeGenericType(typeof(Dictionary<,>), keyType, valueType); @@ -83,7 +87,7 @@ namespace Newtonsoft.Json.Serialization internal IWrappedDictionary CreateWrapper(object dictionary) { - if (dictionary is IDictionary) + if (dictionary is IDictionary && (DictionaryValueType == null || !_isDictionaryValueTypeNullableType)) return new DictionaryWrapper((IDictionary)dictionary); if (_genericWrapperType == null) diff --git a/Src/Newtonsoft.Json/Utilities/LateBoundReflectionDelegateFactory.cs b/Src/Newtonsoft.Json/Utilities/LateBoundReflectionDelegateFactory.cs index 11ae6904..c96be702 100644 --- a/Src/Newtonsoft.Json/Utilities/LateBoundReflectionDelegateFactory.cs +++ b/Src/Newtonsoft.Json/Utilities/LateBoundReflectionDelegateFactory.cs @@ -31,7 +31,12 @@ namespace Newtonsoft.Json.Utilities { internal class LateBoundReflectionDelegateFactory : ReflectionDelegateFactory { - public static readonly LateBoundReflectionDelegateFactory Instance = new LateBoundReflectionDelegateFactory(); + private static readonly LateBoundReflectionDelegateFactory _instance = new LateBoundReflectionDelegateFactory(); + + internal static ReflectionDelegateFactory Instance + { + get { return _instance; } + } public override MethodCall CreateMethodCall(MethodBase method) {