-Improved support for deserializing objects using non-default constructors
-JsonConverterAttribute now allowed on constructor parameters -JsonPropertyAttribute now allowed on constructor parameters
This commit is contained in:
+4
-4
@@ -4,8 +4,8 @@
|
||||
$version = GetVersion $majorVersion
|
||||
$signAssemblies = $false
|
||||
$signKeyPath = "D:\Development\Releases\newtonsoft.snk"
|
||||
$buildDocumentation = $true
|
||||
$buildNuGet = $true
|
||||
$buildDocumentation = $false
|
||||
$buildNuGet = $false
|
||||
|
||||
$baseDir = resolve-path ..
|
||||
$buildDir = "$baseDir\Build"
|
||||
@@ -15,10 +15,10 @@
|
||||
$releaseDir = "$baseDir\Release"
|
||||
$workingDir = "$baseDir\Working"
|
||||
$builds = @(
|
||||
@{Name = "Newtonsoft.Json"; TestsName = "Newtonsoft.Json.Tests"; Constants=""; FinalDir="Net"; NuGetDir = "net40-client,net40-full"; Framework="net-4.0"},
|
||||
@{Name = "Newtonsoft.Json"; TestsName = "Newtonsoft.Json.Tests"; Constants=""; FinalDir="Net"; NuGetDir = "net40"; Framework="net-4.0"},
|
||||
@{Name = "Newtonsoft.Json.WindowsPhone"; TestsName = $null; Constants="SILVERLIGHT;WINDOWS_PHONE"; FinalDir="WindowsPhone"; NuGetDir = "sl3-wp"; Framework="net-4.0"},
|
||||
@{Name = "Newtonsoft.Json.Silverlight"; TestsName = "Newtonsoft.Json.Tests.Silverlight"; Constants="SILVERLIGHT"; FinalDir="Silverlight"; NuGetDir = "sl4"; Framework="net-4.0"},
|
||||
@{Name = "Newtonsoft.Json.Net35"; TestsName = "Newtonsoft.Json.Tests.Net35"; Constants="NET35"; FinalDir="Net35"; NuGetDir = "net35-client,net35-full"; Framework="net-2.0"},
|
||||
@{Name = "Newtonsoft.Json.Net35"; TestsName = "Newtonsoft.Json.Tests.Net35"; Constants="NET35"; FinalDir="Net35"; NuGetDir = "net35"; Framework="net-2.0"},
|
||||
@{Name = "Newtonsoft.Json.Net20"; TestsName = "Newtonsoft.Json.Tests.Net20"; Constants="NET20"; FinalDir="Net20"; NuGetDir = "net20"; Framework="net-2.0"}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -126,6 +126,10 @@
|
||||
<Compile Include="TestObjects\PrivateConstructorTestClass.cs" />
|
||||
<Compile Include="TestObjects\PrivateConstructorWithPublicParametizedConstructorTestClass.cs" />
|
||||
<Compile Include="TestObjects\PropertyCase.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorRequiringConverterTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorWithNonPropertyParameterTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorWithPropertyNameConflict.cs" />
|
||||
<Compile Include="TestObjects\RequestOnly.cs" />
|
||||
<Compile Include="TestObjects\RoleTransfer.cs" />
|
||||
<Compile Include="Serialization\SerializationErrorHandlingTests.cs" />
|
||||
|
||||
@@ -149,6 +149,8 @@
|
||||
<Compile Include="TestObjects\HolderClass.cs" />
|
||||
<Compile Include="TestObjects\ListOfIds.cs" />
|
||||
<Compile Include="TestObjects\Movie.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorRequiringConverterTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorTestClass.cs" />
|
||||
<Compile Include="TestObjects\PersonError.cs" />
|
||||
<Compile Include="TestObjects\PersonPropertyClass.cs" />
|
||||
<Compile Include="TestObjects\PrivateConstructorTestClass.cs" />
|
||||
@@ -177,6 +179,8 @@
|
||||
<Compile Include="TestObjects\LogEntry.cs" />
|
||||
<Compile Include="TestObjects\NonRequest.cs" />
|
||||
<Compile Include="TestObjects\ObjectArrayPropertyTest.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorWithNonPropertyParameterTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorWithPropertyNameConflict.cs" />
|
||||
<Compile Include="TestObjects\SearchResult.cs" />
|
||||
<Compile Include="TestObjects\StructTest.cs" />
|
||||
<Compile Include="TestObjects\WagePerson.cs" />
|
||||
|
||||
@@ -192,6 +192,10 @@
|
||||
<Compile Include="TestObjects\PrivateConstructorWithPublicParametizedConstructorTestClass.cs" />
|
||||
<Compile Include="TestObjects\PrivateMembersClass.cs" />
|
||||
<Compile Include="TestObjects\PropertyCase.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorRequiringConverterTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorWithNonPropertyParameterTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorWithPropertyNameConflict.cs" />
|
||||
<Compile Include="TestObjects\RequestOnly.cs" />
|
||||
<Compile Include="TestObjects\RequiredMembersClass.cs" />
|
||||
<Compile Include="TestObjects\RoleTransfer.cs" />
|
||||
|
||||
@@ -154,6 +154,10 @@
|
||||
<Compile Include="TestObjects\PrivateConstructorWithPublicParametizedConstructorTestClass.cs" />
|
||||
<Compile Include="TestObjects\PrivateMembersClass.cs" />
|
||||
<Compile Include="TestObjects\PropertyCase.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorRequiringConverterTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorWithNonPropertyParameterTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorWithPropertyNameConflict.cs" />
|
||||
<Compile Include="TestObjects\RequestOnly.cs" />
|
||||
<Compile Include="TestObjects\RequiredMembersClass.cs" />
|
||||
<Compile Include="TestObjects\RoleTransfer.cs" />
|
||||
|
||||
@@ -183,6 +183,10 @@
|
||||
<Compile Include="TestObjects\LogEntry.cs" />
|
||||
<Compile Include="TestObjects\NonRequest.cs" />
|
||||
<Compile Include="TestObjects\ObjectArrayPropertyTest.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorRequiringConverterTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorWithNonPropertyParameterTestClass.cs" />
|
||||
<Compile Include="TestObjects\PublicParametizedConstructorWithPropertyNameConflict.cs" />
|
||||
<Compile Include="TestObjects\SearchResult.cs" />
|
||||
<Compile Include="TestObjects\StructTest.cs" />
|
||||
<Compile Include="TestObjects\WagePerson.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.13622")]
|
||||
[assembly: AssemblyFileVersion("4.0.2.13702")]
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json.Tests.TestObjects;
|
||||
using NUnit.Framework;
|
||||
|
||||
@@ -55,5 +52,75 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
Assert.AreEqual("Name!", c.Name);
|
||||
Assert.AreEqual(1, c.Age);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SuccessWithPublicParametizedConstructor()
|
||||
{
|
||||
string json = @"{Name:""Name!""}";
|
||||
|
||||
var c = JsonConvert.DeserializeObject<PublicParametizedConstructorTestClass>(json);
|
||||
Assert.IsNotNull(c);
|
||||
Assert.AreEqual("Name!", c.Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SuccessWithPublicParametizedConstructorWhenParamaterIsNotAProperty()
|
||||
{
|
||||
string json = @"{nameParameter:""Name!""}";
|
||||
|
||||
PublicParametizedConstructorWithNonPropertyParameterTestClass c = JsonConvert.DeserializeObject<PublicParametizedConstructorWithNonPropertyParameterTestClass>(json);
|
||||
Assert.IsNotNull(c);
|
||||
Assert.AreEqual("Name!", c.Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SuccessWithPublicParametizedConstructorWhenParamaterRequiresAConverter()
|
||||
{
|
||||
string json = @"{nameParameter:""Name!""}";
|
||||
|
||||
PublicParametizedConstructorRequiringConverterTestClass c = JsonConvert.DeserializeObject<PublicParametizedConstructorRequiringConverterTestClass>(json, new NameContainerConverter());
|
||||
Assert.IsNotNull(c);
|
||||
Assert.AreEqual("Name!", c.Name.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SuccessWithPublicParametizedConstructorWhenParamaterRequiresAConverterWithParameterAttribute()
|
||||
{
|
||||
string json = @"{nameParameter:""Name!""}";
|
||||
|
||||
PublicParametizedConstructorRequiringConverterWithParameterAttributeTestClass c = JsonConvert.DeserializeObject<PublicParametizedConstructorRequiringConverterWithParameterAttributeTestClass>(json);
|
||||
Assert.IsNotNull(c);
|
||||
Assert.AreEqual("Name!", c.Name.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SuccessWithPublicParametizedConstructorWhenParamaterRequiresAConverterWithPropertyAttribute()
|
||||
{
|
||||
string json = @"{name:""Name!""}";
|
||||
|
||||
PublicParametizedConstructorRequiringConverterWithPropertyAttributeTestClass c = JsonConvert.DeserializeObject<PublicParametizedConstructorRequiringConverterWithPropertyAttributeTestClass>(json);
|
||||
Assert.IsNotNull(c);
|
||||
Assert.AreEqual("Name!", c.Name.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SuccessWithPublicParametizedConstructorWhenParamaterNameConflictsWithPropertyName()
|
||||
{
|
||||
string json = @"{name:""1""}";
|
||||
|
||||
PublicParametizedConstructorWithPropertyNameConflict c = JsonConvert.DeserializeObject<PublicParametizedConstructorWithPropertyNameConflict>(json);
|
||||
Assert.IsNotNull(c);
|
||||
Assert.AreEqual(1, c.Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PublicParametizedConstructorWithPropertyNameConflictWithAttribute()
|
||||
{
|
||||
string json = @"{name:""1""}";
|
||||
|
||||
PublicParametizedConstructorWithPropertyNameConflictWithAttribute c = JsonConvert.DeserializeObject<PublicParametizedConstructorWithPropertyNameConflictWithAttribute>(json);
|
||||
Assert.IsNotNull(c);
|
||||
Assert.AreEqual(1, c.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Newtonsoft.Json.Tests.TestObjects
|
||||
{
|
||||
public class NameContainer
|
||||
{
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
public class NameContainerConverter : JsonConverter
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
NameContainer nameContainer = value as NameContainer;
|
||||
|
||||
if (nameContainer != null)
|
||||
writer.WriteValue(nameContainer.Value);
|
||||
else
|
||||
writer.WriteNull();
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
NameContainer nameContainer = new NameContainer();
|
||||
nameContainer.Value = (string)reader.Value;
|
||||
|
||||
return nameContainer;
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(NameContainer);
|
||||
}
|
||||
}
|
||||
|
||||
public class PublicParametizedConstructorRequiringConverterTestClass
|
||||
{
|
||||
private readonly NameContainer _nameContainer;
|
||||
|
||||
public PublicParametizedConstructorRequiringConverterTestClass(NameContainer nameParameter)
|
||||
{
|
||||
_nameContainer = nameParameter;
|
||||
}
|
||||
|
||||
public NameContainer Name
|
||||
{
|
||||
get { return _nameContainer; }
|
||||
}
|
||||
}
|
||||
|
||||
public class PublicParametizedConstructorRequiringConverterWithParameterAttributeTestClass
|
||||
{
|
||||
private readonly NameContainer _nameContainer;
|
||||
|
||||
public PublicParametizedConstructorRequiringConverterWithParameterAttributeTestClass([JsonConverter(typeof(NameContainerConverter))] NameContainer nameParameter)
|
||||
{
|
||||
_nameContainer = nameParameter;
|
||||
}
|
||||
|
||||
public NameContainer Name
|
||||
{
|
||||
get { return _nameContainer; }
|
||||
}
|
||||
}
|
||||
|
||||
public class PublicParametizedConstructorRequiringConverterWithPropertyAttributeTestClass
|
||||
{
|
||||
private readonly NameContainer _nameContainer;
|
||||
|
||||
public PublicParametizedConstructorRequiringConverterWithPropertyAttributeTestClass(NameContainer name)
|
||||
{
|
||||
_nameContainer = name;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(NameContainerConverter))]
|
||||
public NameContainer Name
|
||||
{
|
||||
get { return _nameContainer; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Newtonsoft.Json.Tests.TestObjects
|
||||
{
|
||||
public class PublicParametizedConstructorTestClass
|
||||
{
|
||||
private readonly string _name;
|
||||
|
||||
public PublicParametizedConstructorTestClass(string name)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Newtonsoft.Json.Tests.TestObjects
|
||||
{
|
||||
public class PublicParametizedConstructorWithNonPropertyParameterTestClass
|
||||
{
|
||||
private readonly string _name;
|
||||
|
||||
public PublicParametizedConstructorWithNonPropertyParameterTestClass(string nameParameter)
|
||||
{
|
||||
_name = nameParameter;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Newtonsoft.Json.Tests.TestObjects
|
||||
{
|
||||
public class PublicParametizedConstructorWithPropertyNameConflict
|
||||
{
|
||||
private readonly int _value;
|
||||
|
||||
public PublicParametizedConstructorWithPropertyNameConflict(string name)
|
||||
{
|
||||
_value = Convert.ToInt32(name);
|
||||
}
|
||||
|
||||
public int Name
|
||||
{
|
||||
get { return _value; }
|
||||
}
|
||||
}
|
||||
|
||||
public class PublicParametizedConstructorWithPropertyNameConflictWithAttribute
|
||||
{
|
||||
private readonly int _value;
|
||||
|
||||
public PublicParametizedConstructorWithPropertyNameConflictWithAttribute([JsonProperty("name")]string nameParameter)
|
||||
{
|
||||
_value = Convert.ToInt32(nameParameter);
|
||||
}
|
||||
|
||||
public int Name
|
||||
{
|
||||
get { return _value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace Newtonsoft.Json
|
||||
/// <summary>
|
||||
/// Instructs the <see cref="JsonSerializer"/> to use the specified <see cref="JsonConverter"/> when serializing the member or class.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Enum, AllowMultiple = false)]
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Enum | AttributeTargets.Parameter, AllowMultiple = false)]
|
||||
public sealed class JsonConverterAttribute : Attribute
|
||||
{
|
||||
private readonly Type _converterType;
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Newtonsoft.Json
|
||||
/// <summary>
|
||||
/// Instructs the <see cref="JsonSerializer"/> to always serialize the member with the specified name.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)]
|
||||
public sealed class JsonPropertyAttribute : Attribute
|
||||
{
|
||||
// yuck. can't set nullable properties on an attribute in C#
|
||||
|
||||
@@ -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.13622")]
|
||||
[assembly: AssemblyFileVersion("4.0.2.13702")]
|
||||
#endif
|
||||
|
||||
[assembly: CLSCompliant(true)]
|
||||
|
||||
@@ -280,11 +280,26 @@ namespace Newtonsoft.Json.Serialization
|
||||
|
||||
contract.MemberSerialization = JsonTypeReflector.GetObjectMemberSerialization(objectType);
|
||||
contract.Properties.AddRange(CreateProperties(contract.UnderlyingType, contract.MemberSerialization));
|
||||
if (objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Any(c => c.IsDefined(typeof(JsonConstructorAttribute), true)))
|
||||
contract.OverrideConstructor = GetAttributeConstructor(objectType);
|
||||
else if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic)
|
||||
contract.ParametrizedConstructor = GetParametrizedConstructor(objectType);
|
||||
|
||||
// check if a JsonConstructorAttribute has been defined and use that
|
||||
if (objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Any(c => c.IsDefined(typeof(JsonConstructorAttribute), true)))
|
||||
{
|
||||
ConstructorInfo constructor = GetAttributeConstructor(objectType);
|
||||
if (constructor != null)
|
||||
{
|
||||
contract.OverrideConstructor = constructor;
|
||||
contract.ConstructorParameters.AddRange(CreateConstructorParameters(constructor, contract.Properties));
|
||||
}
|
||||
}
|
||||
else if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic)
|
||||
{
|
||||
ConstructorInfo constructor = GetParametrizedConstructor(objectType);
|
||||
if (constructor != null)
|
||||
{
|
||||
contract.ParametrizedConstructor = constructor;
|
||||
contract.ConstructorParameters.AddRange(CreateConstructorParameters(constructor, contract.Properties));
|
||||
}
|
||||
}
|
||||
return contract;
|
||||
}
|
||||
|
||||
@@ -310,6 +325,72 @@ namespace Newtonsoft.Json.Serialization
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the constructor parameters.
|
||||
/// </summary>
|
||||
/// <param name="constructor">The constructor to create properties for.</param>
|
||||
/// <param name="memberProperties">The type's member properties.</param>
|
||||
/// <returns>Properties for the given <see cref="ConstructorInfo"/>.</returns>
|
||||
protected virtual IList<JsonProperty> CreateConstructorParameters(ConstructorInfo constructor, JsonPropertyCollection memberProperties)
|
||||
{
|
||||
var constructorParameters = constructor.GetParameters();
|
||||
|
||||
JsonPropertyCollection parameterCollection = new JsonPropertyCollection(constructor.DeclaringType);
|
||||
|
||||
foreach (ParameterInfo parameterInfo in constructorParameters)
|
||||
{
|
||||
JsonProperty matchingMemberProperty = memberProperties.GetClosestMatchProperty(parameterInfo.Name);
|
||||
// type must match as well as name
|
||||
if (matchingMemberProperty != null && matchingMemberProperty.PropertyType != parameterInfo.ParameterType)
|
||||
matchingMemberProperty = null;
|
||||
|
||||
JsonProperty property = CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo);
|
||||
|
||||
if (property != null)
|
||||
{
|
||||
parameterCollection.AddProperty(property);
|
||||
}
|
||||
}
|
||||
|
||||
return parameterCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="JsonProperty"/> for the given <see cref="ParameterInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="matchingMemberProperty">The matching member property.</param>
|
||||
/// <param name="parameterInfo">The constructor parameter.</param>
|
||||
/// <returns>A created <see cref="JsonProperty"/> for the given <see cref="ParameterInfo"/>.</returns>
|
||||
protected virtual JsonProperty CreatePropertyFromConstructorParameter(JsonProperty matchingMemberProperty, ParameterInfo parameterInfo)
|
||||
{
|
||||
JsonProperty property = new JsonProperty();
|
||||
property.PropertyType = parameterInfo.ParameterType;
|
||||
|
||||
bool allowNonPublicAccess;
|
||||
SetPropertySettingsFromAttributes(property, parameterInfo, parameterInfo.Name, parameterInfo.Member.DeclaringType, MemberSerialization.OptOut, out allowNonPublicAccess);
|
||||
|
||||
property.Readable = false;
|
||||
property.Writable = true;
|
||||
|
||||
// "inherit" values from matching member property if unset on parameter
|
||||
if (matchingMemberProperty != null)
|
||||
{
|
||||
property.PropertyName = (property.PropertyName != parameterInfo.Name) ? property.PropertyName : matchingMemberProperty.PropertyName;
|
||||
property.Converter = property.Converter ?? matchingMemberProperty.Converter;
|
||||
property.MemberConverter = property.MemberConverter ?? matchingMemberProperty.MemberConverter;
|
||||
property.DefaultValue = property.DefaultValue ?? matchingMemberProperty.DefaultValue;
|
||||
property.Required = (property.Required != Required.Default) ? property.Required : matchingMemberProperty.Required;
|
||||
property.IsReference = property.IsReference ?? matchingMemberProperty.IsReference;
|
||||
property.NullValueHandling = property.NullValueHandling ?? matchingMemberProperty.NullValueHandling;
|
||||
property.DefaultValueHandling = property.DefaultValueHandling ?? matchingMemberProperty.DefaultValueHandling;
|
||||
property.ReferenceLoopHandling = property.ReferenceLoopHandling ?? matchingMemberProperty.ReferenceLoopHandling;
|
||||
property.ObjectCreationHandling = property.ObjectCreationHandling ?? matchingMemberProperty.ObjectCreationHandling;
|
||||
property.TypeNameHandling = property.TypeNameHandling ?? matchingMemberProperty.TypeNameHandling;
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the default <see cref="JsonConverter" /> for the contract.
|
||||
/// </summary>
|
||||
@@ -667,22 +748,32 @@ namespace Newtonsoft.Json.Serialization
|
||||
property.PropertyType = ReflectionUtils.GetMemberUnderlyingType(member);
|
||||
property.ValueProvider = CreateMemberValueProvider(member);
|
||||
|
||||
// resolve converter for property
|
||||
// the class type might have a converter but the property converter takes presidence
|
||||
property.Converter = JsonTypeReflector.GetJsonConverter(member, property.PropertyType);
|
||||
bool allowNonPublicAccess;
|
||||
SetPropertySettingsFromAttributes(property, member, member.Name, member.DeclaringType, memberSerialization, out allowNonPublicAccess);
|
||||
|
||||
property.Readable = ReflectionUtils.CanReadMemberValue(member, allowNonPublicAccess);
|
||||
property.Writable = ReflectionUtils.CanSetMemberValue(member, allowNonPublicAccess);
|
||||
property.ShouldSerialize = CreateShouldSerializeTest(member);
|
||||
|
||||
SetIsSpecifiedActions(property, member);
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
private void SetPropertySettingsFromAttributes(JsonProperty property, ICustomAttributeProvider attributeProvider, string name, Type declaringType, MemberSerialization memberSerialization, out bool allowNonPublicAccess)
|
||||
{
|
||||
#if !PocketPC && !NET20
|
||||
DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(member.DeclaringType);
|
||||
DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(declaringType);
|
||||
|
||||
DataMemberAttribute dataMemberAttribute;
|
||||
if (dataContractAttribute != null)
|
||||
dataMemberAttribute = JsonTypeReflector.GetAttribute<DataMemberAttribute>(member);
|
||||
dataMemberAttribute = JsonTypeReflector.GetAttribute<DataMemberAttribute>(attributeProvider);
|
||||
else
|
||||
dataMemberAttribute = null;
|
||||
#endif
|
||||
|
||||
JsonPropertyAttribute propertyAttribute = JsonTypeReflector.GetAttribute<JsonPropertyAttribute>(member);
|
||||
bool hasIgnoreAttribute = (JsonTypeReflector.GetAttribute<JsonIgnoreAttribute>(member) != null);
|
||||
JsonPropertyAttribute propertyAttribute = JsonTypeReflector.GetAttribute<JsonPropertyAttribute>(attributeProvider);
|
||||
bool hasIgnoreAttribute = (JsonTypeReflector.GetAttribute<JsonIgnoreAttribute>(attributeProvider) != null);
|
||||
|
||||
string mappedName;
|
||||
if (propertyAttribute != null && propertyAttribute.PropertyName != null)
|
||||
@@ -692,9 +783,10 @@ namespace Newtonsoft.Json.Serialization
|
||||
mappedName = dataMemberAttribute.Name;
|
||||
#endif
|
||||
else
|
||||
mappedName = member.Name;
|
||||
mappedName = name;
|
||||
|
||||
property.PropertyName = ResolvePropertyName(mappedName);
|
||||
property.UnderlyingName = name;
|
||||
|
||||
if (propertyAttribute != null)
|
||||
property.Required = propertyAttribute.Required;
|
||||
@@ -713,22 +805,12 @@ namespace Newtonsoft.Json.Serialization
|
||||
#endif
|
||||
));
|
||||
|
||||
bool allowNonPublicAccess = false;
|
||||
if ((DefaultMembersSearchFlags & BindingFlags.NonPublic) == BindingFlags.NonPublic)
|
||||
allowNonPublicAccess = true;
|
||||
if (propertyAttribute != null)
|
||||
allowNonPublicAccess = true;
|
||||
#if !PocketPC && !NET20
|
||||
if (dataMemberAttribute != null)
|
||||
allowNonPublicAccess = true;
|
||||
#endif
|
||||
// resolve converter for property
|
||||
// the class type might have a converter but the property converter takes presidence
|
||||
property.Converter = JsonTypeReflector.GetJsonConverter(attributeProvider, property.PropertyType);
|
||||
property.MemberConverter = JsonTypeReflector.GetJsonConverter(attributeProvider, property.PropertyType);
|
||||
|
||||
property.Readable = ReflectionUtils.CanReadMemberValue(member, allowNonPublicAccess);
|
||||
property.Writable = ReflectionUtils.CanSetMemberValue(member, allowNonPublicAccess);
|
||||
|
||||
property.MemberConverter = JsonTypeReflector.GetJsonConverter(member, ReflectionUtils.GetMemberUnderlyingType(member));
|
||||
|
||||
DefaultValueAttribute defaultValueAttribute = JsonTypeReflector.GetAttribute<DefaultValueAttribute>(member);
|
||||
DefaultValueAttribute defaultValueAttribute = JsonTypeReflector.GetAttribute<DefaultValueAttribute>(attributeProvider);
|
||||
property.DefaultValue = (defaultValueAttribute != null) ? defaultValueAttribute.Value : null;
|
||||
|
||||
property.NullValueHandling = (propertyAttribute != null) ? propertyAttribute._nullValueHandling : null;
|
||||
@@ -738,11 +820,15 @@ namespace Newtonsoft.Json.Serialization
|
||||
property.TypeNameHandling = (propertyAttribute != null) ? propertyAttribute._typeNameHandling : null;
|
||||
property.IsReference = (propertyAttribute != null) ? propertyAttribute._isReference : null;
|
||||
|
||||
property.ShouldSerialize = CreateShouldSerializeTest(member);
|
||||
|
||||
SetIsSpecifiedActions(property, member);
|
||||
|
||||
return property;
|
||||
allowNonPublicAccess = false;
|
||||
if ((DefaultMembersSearchFlags & BindingFlags.NonPublic) == BindingFlags.NonPublic)
|
||||
allowNonPublicAccess = true;
|
||||
if (propertyAttribute != null)
|
||||
allowNonPublicAccess = true;
|
||||
#if !PocketPC && !NET20
|
||||
if (dataMemberAttribute != null)
|
||||
allowNonPublicAccess = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
private Predicate<object> CreateShouldSerializeTest(MemberInfo member)
|
||||
|
||||
@@ -44,6 +44,11 @@ namespace Newtonsoft.Json.Serialization
|
||||
/// </summary>
|
||||
/// <value>The object's properties.</value>
|
||||
public JsonPropertyCollection Properties { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the constructor parameters required for any non-default constructor
|
||||
/// </summary>
|
||||
public JsonPropertyCollection ConstructorParameters { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the override constructor used to create the object.
|
||||
@@ -66,7 +71,8 @@ namespace Newtonsoft.Json.Serialization
|
||||
public JsonObjectContract(Type underlyingType)
|
||||
: base(underlyingType)
|
||||
{
|
||||
Properties = new JsonPropertyCollection(UnderlyingType);
|
||||
Properties = new JsonPropertyCollection(UnderlyingType);
|
||||
ConstructorParameters = new JsonPropertyCollection(UnderlyingType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,16 +28,22 @@ using System;
|
||||
namespace Newtonsoft.Json.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps a JSON property to a .NET member.
|
||||
/// Maps a JSON property to a .NET member or constructor parameter.
|
||||
/// </summary>
|
||||
public class JsonProperty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the property.
|
||||
/// Gets or sets the name of the property.
|
||||
/// </summary>
|
||||
/// <value>The name of the property.</value>
|
||||
public string PropertyName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the underlying member or parameter.
|
||||
/// </summary>
|
||||
/// <value>The name of the underlying member or parameter.</value>
|
||||
public string UnderlyingName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IValueProvider"/> that will get and set the <see cref="JsonProperty"/> during serialization.
|
||||
/// </summary>
|
||||
@@ -57,6 +63,12 @@ namespace Newtonsoft.Json.Serialization
|
||||
/// <value>The converter.</value>
|
||||
public JsonConverter Converter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the member converter.
|
||||
/// </summary>
|
||||
/// <value>The member converter.</value>
|
||||
public JsonConverter MemberConverter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="JsonProperty"/> is ignored.
|
||||
/// </summary>
|
||||
@@ -75,12 +87,6 @@ namespace Newtonsoft.Json.Serialization
|
||||
/// <value><c>true</c> if writable; otherwise, <c>false</c>.</value>
|
||||
public bool Writable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the member converter.
|
||||
/// </summary>
|
||||
/// <value>The member converter.</value>
|
||||
public JsonConverter MemberConverter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default value.
|
||||
/// </summary>
|
||||
|
||||
@@ -43,10 +43,11 @@ namespace Newtonsoft.Json.Serialization
|
||||
{
|
||||
private JsonSerializerProxy _internalSerializer;
|
||||
#if !SILVERLIGHT && !PocketPC
|
||||
private JsonFormatterConverter _formatterConverter;
|
||||
private JsonFormatterConverter _formatterConverter;
|
||||
#endif
|
||||
|
||||
public JsonSerializerInternalReader(JsonSerializer serializer) : base(serializer)
|
||||
public JsonSerializerInternalReader(JsonSerializer serializer)
|
||||
: base(serializer)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -81,9 +82,9 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
|
||||
if (contract is JsonDictionaryContract)
|
||||
PopulateDictionary(CollectionUtils.CreateDictionaryWrapper(target), reader, (JsonDictionaryContract) contract, id);
|
||||
PopulateDictionary(CollectionUtils.CreateDictionaryWrapper(target), reader, (JsonDictionaryContract)contract, id);
|
||||
else if (contract is JsonObjectContract)
|
||||
PopulateObject(target, reader, (JsonObjectContract) contract, id);
|
||||
PopulateObject(target, reader, (JsonObjectContract)contract, id);
|
||||
else
|
||||
throw new JsonSerializationException("Cannot populate JSON object onto type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType));
|
||||
}
|
||||
@@ -248,7 +249,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
return constructorName;
|
||||
case JsonToken.Null:
|
||||
case JsonToken.Undefined:
|
||||
if (objectType == typeof (DBNull))
|
||||
if (objectType == typeof(DBNull))
|
||||
return DBNull.Value;
|
||||
|
||||
return EnsureType(reader.Value, CultureInfo.InvariantCulture, objectType);
|
||||
@@ -413,7 +414,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
return CreateDynamic(reader, dynamicContract, id);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
throw new JsonSerializationException("Cannot deserialize JSON object into type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType));
|
||||
}
|
||||
|
||||
@@ -456,11 +457,11 @@ namespace Newtonsoft.Json.Serialization
|
||||
|
||||
private bool HasDefinedType(Type type)
|
||||
{
|
||||
return (type != null && type != typeof (object) && !typeof(JToken).IsAssignableFrom(type)
|
||||
return (type != null && type != typeof(object) && !typeof(JToken).IsAssignableFrom(type)
|
||||
#if !(NET35 || NET20 || WINDOWS_PHONE)
|
||||
&& type != typeof(IDynamicMetaObjectProvider)
|
||||
&& type != typeof(IDynamicMetaObjectProvider)
|
||||
#endif
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
private object EnsureType(object value, CultureInfo culture, Type targetType)
|
||||
@@ -638,7 +639,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
break;
|
||||
case JsonToken.EndObject:
|
||||
contract.InvokeOnDeserialized(dictionary.UnderlyingDictionary, Serializer.Context);
|
||||
|
||||
|
||||
return dictionary.UnderlyingDictionary;
|
||||
default:
|
||||
throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType);
|
||||
@@ -788,7 +789,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
Serializer.ReferenceResolver.AddReference(this, id, newObject);
|
||||
|
||||
contract.InvokeOnDeserializing(newObject, Serializer.Context);
|
||||
|
||||
|
||||
bool exit = false;
|
||||
do
|
||||
{
|
||||
@@ -807,7 +808,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
else
|
||||
{
|
||||
Type t = (JsonReader.IsPrimitiveToken(reader.TokenType)) ? reader.ValueType : typeof (IDynamicMetaObjectProvider);
|
||||
Type t = (JsonReader.IsPrimitiveToken(reader.TokenType)) ? reader.ValueType : typeof(IDynamicMetaObjectProvider);
|
||||
|
||||
object value = CreateValueNonProperty(reader, t, GetContractSafe(t, null));
|
||||
|
||||
@@ -865,57 +866,14 @@ namespace Newtonsoft.Json.Serialization
|
||||
|
||||
Type objectType = contract.UnderlyingType;
|
||||
|
||||
IDictionary<JsonProperty, object> propertyValues = new Dictionary<JsonProperty, object>();
|
||||
|
||||
bool exit = false;
|
||||
do
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.PropertyName:
|
||||
string memberName = reader.Value.ToString();
|
||||
|
||||
// attempt exact case match first
|
||||
// then try match ignoring case
|
||||
JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName);
|
||||
|
||||
if (property != null)
|
||||
{
|
||||
if (!ReadForType(reader, property.PropertyType, property.Converter))
|
||||
throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName));
|
||||
|
||||
if (!property.Ignored)
|
||||
propertyValues[property] = CreateValueProperty(reader, property, null, true, null);
|
||||
else
|
||||
reader.Skip();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!reader.Read())
|
||||
throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName));
|
||||
|
||||
if (Serializer.MissingMemberHandling == MissingMemberHandling.Error)
|
||||
throw new JsonSerializationException("Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, objectType.Name));
|
||||
|
||||
reader.Skip();
|
||||
}
|
||||
break;
|
||||
case JsonToken.Comment:
|
||||
break;
|
||||
case JsonToken.EndObject:
|
||||
exit = true;
|
||||
break;
|
||||
default:
|
||||
throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType);
|
||||
}
|
||||
} while (!exit && reader.Read());
|
||||
IDictionary<JsonProperty, object> propertyValues = ResolvePropertyAndConstructorValues(contract, reader, objectType);
|
||||
|
||||
IDictionary<ParameterInfo, object> constructorParameters = constructorInfo.GetParameters().ToDictionary(p => p, p => (object)null);
|
||||
IDictionary<JsonProperty, object> remainingPropertyValues = new Dictionary<JsonProperty, object>();
|
||||
|
||||
foreach (KeyValuePair<JsonProperty, object> propertyValue in propertyValues)
|
||||
{
|
||||
ParameterInfo matchingConstructorParameter = constructorParameters.ForgivingCaseSensitiveFind(kv => kv.Key.Name, propertyValue.Key.PropertyName).Key;
|
||||
ParameterInfo matchingConstructorParameter = constructorParameters.ForgivingCaseSensitiveFind(kv => kv.Key.Name, propertyValue.Key.UnderlyingName).Key;
|
||||
if (matchingConstructorParameter != null)
|
||||
constructorParameters[matchingConstructorParameter] = propertyValue.Value;
|
||||
else
|
||||
@@ -943,6 +901,54 @@ namespace Newtonsoft.Json.Serialization
|
||||
return createdObject;
|
||||
}
|
||||
|
||||
private IDictionary<JsonProperty, object> ResolvePropertyAndConstructorValues(JsonObjectContract contract, JsonReader reader, Type objectType)
|
||||
{
|
||||
IDictionary<JsonProperty, object> propertyValues = new Dictionary<JsonProperty, object>();
|
||||
bool exit = false;
|
||||
do
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.PropertyName:
|
||||
string memberName = reader.Value.ToString();
|
||||
|
||||
// attempt exact case match first
|
||||
// then try match ignoring case
|
||||
JsonProperty property = contract.ConstructorParameters.GetClosestMatchProperty(memberName) ??
|
||||
contract.Properties.GetClosestMatchProperty(memberName);
|
||||
|
||||
if (property != null)
|
||||
{
|
||||
if (!ReadForType(reader, property.PropertyType, property.Converter))
|
||||
throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName));
|
||||
|
||||
if (!property.Ignored)
|
||||
propertyValues[property] = CreateValueProperty(reader, property, null, true, null);
|
||||
else
|
||||
reader.Skip();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!reader.Read())
|
||||
throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName));
|
||||
|
||||
if (Serializer.MissingMemberHandling == MissingMemberHandling.Error)
|
||||
throw new JsonSerializationException("Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, objectType.Name));
|
||||
|
||||
reader.Skip();
|
||||
}
|
||||
break;
|
||||
case JsonToken.EndObject:
|
||||
exit = true;
|
||||
break;
|
||||
default:
|
||||
throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType);
|
||||
}
|
||||
} while (!exit && reader.Read());
|
||||
|
||||
return propertyValues;
|
||||
}
|
||||
|
||||
private bool ReadForType(JsonReader reader, Type t, JsonConverter propertyConverter)
|
||||
{
|
||||
// don't read properties with converters as a specific value
|
||||
|
||||
Reference in New Issue
Block a user