-Documentation

-Fixed JObject loading JSON with comments
-Fixed error when using a Specified property with no setter
-Fixed transient constructor error on Windows Phone 7
-Fixed deserializing null values into nullable generic dictionaries
This commit is contained in:
JamesNK
2011-10-01 04:41:09 +00:00
parent 7d3ce867da
commit 3baf856f92
10 changed files with 118 additions and 45 deletions
+35 -21
View File
@@ -211,12 +211,12 @@
<h4>
<strong>Objects</strong></h4>
<p>
.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.</p>
<p>
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.</p>
@@ -251,7 +251,7 @@
<p>
.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.</p>
<p>
@@ -263,40 +263,54 @@
<h4>
<strong>Untyped Objects</strong></h4>
<p>
.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).</p>
<p>
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.
</p>
<h4>
<strong>Dynamic</strong></h4>
<p>
.NET dynamic objects (introduced in .NET 4) are serialized as JSON objects. A property
is written for every member name returned by IDynamicMetaObjectProvider.GetDynamicMemberNames.</p>
<p>
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.</p>
<p>
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.
</p>
<p>
The second usage of dynamic in .NET are classes that implement <a href="http://msdn.microsoft.com/en-us/library/system.dynamic.idynamicmetaobjectprovider.aspx"
target="_blank">IDynamicMetaObjectProvider</a>. This interface lets the implementor
create dynamic objects that intercept the property and method calls on an object
and use them. <a href="http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx"
target="_blank">ExpandoObject</a> is a good example of a dynamic object.
</p>
<p>
Dynamic objects are serialized as JSON objects. A property is written for every
member name returned by <a href="http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicmetaobject.getdynamicmembernames.aspx"
target="_blank">DynamicMetaObject.GetDynamicMemberNames</a>.</p>
<p>
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</p>
<h4>
<strong>ISerializable</strong></h4>
<p>
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.</p>
StreamingContext is called, passing the JSON object's values.</p>
<p>
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.
</p>
<h4>
<strong>LINQ to JSON</strong></h4>
@@ -313,7 +327,7 @@
all other tests.</p>
<p>
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.</p>
+5 -5
View File
@@ -178,7 +178,7 @@ DefaultValueHandling controls how Json.NET uses default values set using the .NE
<td>
<div class="summary">
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.
</div>
@@ -192,8 +192,8 @@ DefaultValueHandling controls how Json.NET uses default values set using the .NE
<td>
<div class="summary">
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.
</div>
<br>
@@ -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.
</div>
<br>
@@ -405,7 +405,7 @@ DefaultValueHandling controls how Json.NET uses default values set using the .NE
<div class="summary">
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.
@@ -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()
@@ -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
@@ -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<SpecifiedTestClass>(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<string, int?> v1 = new Dictionary<string, int?>
{
{ "First", 1 },
{ "Second", null },
{ "Third", 3 }
};
string json = JsonConvert.SerializeObject(v1, Formatting.Indented);
Assert.AreEqual(@"{
""First"": 1,
""Second"": null,
""Third"": 3
}", json);
IDictionary<string, int?> v2 = JsonConvert.DeserializeObject<IDictionary<string, int?>>(json);
Assert.AreEqual(3, v2.Count);
Assert.AreEqual(1, v2["First"]);
Assert.AreEqual(null, v2["Second"]);
Assert.AreEqual(3, v2["Third"]);
}
}
public class UriGuidTimeSpanTestClass
+9 -10
View File
@@ -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
}
}
/// <summary>
/// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <value></value>
/// <returns>The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</returns>
public int Count
{
get { return ChildrenTokens.Count; }
}
bool ICollection<KeyValuePair<string,JToken>>.IsReadOnly
{
get { return false; }
@@ -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)]
@@ -78,7 +78,11 @@ namespace Newtonsoft.Json.Serialization
/// </summary>
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<JsonConverter> BuiltInConverters = new List<JsonConverter>
{
#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<object, object> specifiedPropertyGet = JsonTypeReflector.ReflectionDelegateFactory.CreateGet<object>(specifiedMember);
property.GetIsSpecified = o => (bool)specifiedPropertyGet(o);
property.SetIsSpecified = JsonTypeReflector.ReflectionDelegateFactory.CreateSet<object>(specifiedMember);
if (ReflectionUtils.CanSetMemberValue(specifiedMember, allowNonPublicAccess, false))
property.SetIsSpecified = JsonTypeReflector.ReflectionDelegateFactory.CreateSet<object>(specifiedMember);
}
/// <summary>
@@ -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<object, object> _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<object, object>((IDictionary)dictionary);
if (_genericWrapperType == null)
@@ -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<T, object> CreateMethodCall<T>(MethodBase method)
{