diff --git a/Build/build.ps1 b/Build/build.ps1 index b94aae8f..934e47b0 100644 --- a/Build/build.ps1 +++ b/Build/build.ps1 @@ -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"} ) } diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj index 5d445750..fd80fef5 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj @@ -126,6 +126,10 @@ + + + + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj index 39811c4b..530768ea 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj @@ -149,6 +149,8 @@ + + @@ -177,6 +179,8 @@ + + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj index fe13d1ac..d47d3f66 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -192,6 +192,10 @@ + + + + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj index 05a89057..f2e59550 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj @@ -154,6 +154,10 @@ + + + + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index 151824d7..8b90838c 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -183,6 +183,10 @@ + + + + diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index c094b45a..89879d42 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.13622")] +[assembly: AssemblyFileVersion("4.0.2.13702")] #endif diff --git a/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs index d6714493..3f2a78e3 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs @@ -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(json); + Assert.IsNotNull(c); + Assert.AreEqual("Name!", c.Name); + } + + [Test] + public void SuccessWithPublicParametizedConstructorWhenParamaterIsNotAProperty() + { + string json = @"{nameParameter:""Name!""}"; + + PublicParametizedConstructorWithNonPropertyParameterTestClass c = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(c); + Assert.AreEqual("Name!", c.Name); + } + + [Test] + public void SuccessWithPublicParametizedConstructorWhenParamaterRequiresAConverter() + { + string json = @"{nameParameter:""Name!""}"; + + PublicParametizedConstructorRequiringConverterTestClass c = JsonConvert.DeserializeObject(json, new NameContainerConverter()); + Assert.IsNotNull(c); + Assert.AreEqual("Name!", c.Name.Value); + } + + [Test] + public void SuccessWithPublicParametizedConstructorWhenParamaterRequiresAConverterWithParameterAttribute() + { + string json = @"{nameParameter:""Name!""}"; + + PublicParametizedConstructorRequiringConverterWithParameterAttributeTestClass c = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(c); + Assert.AreEqual("Name!", c.Name.Value); + } + + [Test] + public void SuccessWithPublicParametizedConstructorWhenParamaterRequiresAConverterWithPropertyAttribute() + { + string json = @"{name:""Name!""}"; + + PublicParametizedConstructorRequiringConverterWithPropertyAttributeTestClass c = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(c); + Assert.AreEqual("Name!", c.Name.Value); + } + + [Test] + public void SuccessWithPublicParametizedConstructorWhenParamaterNameConflictsWithPropertyName() + { + string json = @"{name:""1""}"; + + PublicParametizedConstructorWithPropertyNameConflict c = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(c); + Assert.AreEqual(1, c.Name); + } + + [Test] + public void PublicParametizedConstructorWithPropertyNameConflictWithAttribute() + { + string json = @"{name:""1""}"; + + PublicParametizedConstructorWithPropertyNameConflictWithAttribute c = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(c); + Assert.AreEqual(1, c.Name); + } } -} +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/PublicParametizedConstructorRequiringConverterTestClass.cs b/Src/Newtonsoft.Json.Tests/TestObjects/PublicParametizedConstructorRequiringConverterTestClass.cs new file mode 100644 index 00000000..0b94146d --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/TestObjects/PublicParametizedConstructorRequiringConverterTestClass.cs @@ -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; } + } + } +} diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/PublicParametizedConstructorTestClass.cs b/Src/Newtonsoft.Json.Tests/TestObjects/PublicParametizedConstructorTestClass.cs new file mode 100644 index 00000000..6cace1ff --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/TestObjects/PublicParametizedConstructorTestClass.cs @@ -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; } + } + } +} diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/PublicParametizedConstructorWithNonPropertyParameterTestClass.cs b/Src/Newtonsoft.Json.Tests/TestObjects/PublicParametizedConstructorWithNonPropertyParameterTestClass.cs new file mode 100644 index 00000000..57dbb649 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/TestObjects/PublicParametizedConstructorWithNonPropertyParameterTestClass.cs @@ -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; } + } + } +} diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/PublicParametizedConstructorWithPropertyNameConflict.cs b/Src/Newtonsoft.Json.Tests/TestObjects/PublicParametizedConstructorWithPropertyNameConflict.cs new file mode 100644 index 00000000..1baaa7e3 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/TestObjects/PublicParametizedConstructorWithPropertyNameConflict.cs @@ -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; } + } + } +} diff --git a/Src/Newtonsoft.Json/JsonConverterAttribute.cs b/Src/Newtonsoft.Json/JsonConverterAttribute.cs index e28fd79b..ae3a8d57 100644 --- a/Src/Newtonsoft.Json/JsonConverterAttribute.cs +++ b/Src/Newtonsoft.Json/JsonConverterAttribute.cs @@ -10,7 +10,7 @@ namespace Newtonsoft.Json /// /// Instructs the to use the specified when serializing the member or class. /// - [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; diff --git a/Src/Newtonsoft.Json/JsonPropertyAttribute.cs b/Src/Newtonsoft.Json/JsonPropertyAttribute.cs index 5df13e69..465d2d51 100644 --- a/Src/Newtonsoft.Json/JsonPropertyAttribute.cs +++ b/Src/Newtonsoft.Json/JsonPropertyAttribute.cs @@ -5,7 +5,7 @@ namespace Newtonsoft.Json /// /// Instructs the to always serialize the member with the specified name. /// - [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# diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 75ed64fc..c67da98a 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.13622")] +[assembly: AssemblyFileVersion("4.0.2.13702")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index c93ffa38..cc1cd298 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -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; } + /// + /// Creates the constructor parameters. + /// + /// The constructor to create properties for. + /// The type's member properties. + /// Properties for the given . + protected virtual IList 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; + } + + /// + /// Creates a for the given . + /// + /// The matching member property. + /// The constructor parameter. + /// A created for the given . + 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; + } + /// /// Resolves the default for the contract. /// @@ -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(member); + dataMemberAttribute = JsonTypeReflector.GetAttribute(attributeProvider); else dataMemberAttribute = null; #endif - JsonPropertyAttribute propertyAttribute = JsonTypeReflector.GetAttribute(member); - bool hasIgnoreAttribute = (JsonTypeReflector.GetAttribute(member) != null); + JsonPropertyAttribute propertyAttribute = JsonTypeReflector.GetAttribute(attributeProvider); + bool hasIgnoreAttribute = (JsonTypeReflector.GetAttribute(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(member); + DefaultValueAttribute defaultValueAttribute = JsonTypeReflector.GetAttribute(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 CreateShouldSerializeTest(MemberInfo member) diff --git a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs index c3d4e6ef..426a469f 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs @@ -44,6 +44,11 @@ namespace Newtonsoft.Json.Serialization /// /// The object's properties. public JsonPropertyCollection Properties { get; private set; } + + /// + /// Gets the constructor parameters required for any non-default constructor + /// + public JsonPropertyCollection ConstructorParameters { get; private set; } /// /// 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); } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonProperty.cs b/Src/Newtonsoft.Json/Serialization/JsonProperty.cs index 0b2512e0..ccc39d0f 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonProperty.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonProperty.cs @@ -28,16 +28,22 @@ using System; namespace Newtonsoft.Json.Serialization { /// - /// Maps a JSON property to a .NET member. + /// Maps a JSON property to a .NET member or constructor parameter. /// public class JsonProperty { /// - /// Gets the name of the property. + /// Gets or sets the name of the property. /// /// The name of the property. public string PropertyName { get; set; } + /// + /// Gets or sets the name of the underlying member or parameter. + /// + /// The name of the underlying member or parameter. + public string UnderlyingName { get; set; } + /// /// Gets the that will get and set the during serialization. /// @@ -57,6 +63,12 @@ namespace Newtonsoft.Json.Serialization /// The converter. public JsonConverter Converter { get; set; } + /// + /// Gets the member converter. + /// + /// The member converter. + public JsonConverter MemberConverter { get; set; } + /// /// Gets a value indicating whether this is ignored. /// @@ -75,12 +87,6 @@ namespace Newtonsoft.Json.Serialization /// true if writable; otherwise, false. public bool Writable { get; set; } - /// - /// Gets the member converter. - /// - /// The member converter. - public JsonConverter MemberConverter { get; set; } - /// /// Gets the default value. /// diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 1ac9ee38..35e4705b 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -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 propertyValues = new Dictionary(); - - 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 propertyValues = ResolvePropertyAndConstructorValues(contract, reader, objectType); IDictionary constructorParameters = constructorInfo.GetParameters().ToDictionary(p => p, p => (object)null); IDictionary remainingPropertyValues = new Dictionary(); foreach (KeyValuePair 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 ResolvePropertyAndConstructorValues(JsonObjectContract contract, JsonReader reader, Type objectType) + { + IDictionary propertyValues = new Dictionary(); + 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