diff --git a/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs index cef0ffdd..f4ff2f84 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs @@ -24,7 +24,10 @@ #endregion using System; +using System.Collections.Generic; +using System.ComponentModel; using System.Reflection; +using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Tests.TestObjects; #if NETFX_CORE using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; @@ -37,6 +40,11 @@ using Assert = Newtonsoft.Json.Tests.XUnitAssert; #else using NUnit.Framework; #endif +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif namespace Newtonsoft.Json.Tests.Serialization { @@ -162,5 +170,79 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.IsNotNull(c); Assert.AreEqual(1, c.Name); } + + public class ConstructorParametersRespectDefaultValueAttributes + { + [DefaultValue("parameter1_default")] + public string Parameter1 { get; private set; } + [DefaultValue("parameter2_default")] + public string Parameter2 { get; private set; } + [DefaultValue("parameter3_default")] + public string Parameter3 { get; set; } + [DefaultValue("parameter4_default")] + public string Parameter4 { get; set; } + + public ConstructorParametersRespectDefaultValueAttributes(string parameter1, string parameter2, string parameter3) + { + Parameter1 = parameter1; + Parameter2 = parameter2; + Parameter3 = parameter3; + } + } + + [Test] + public void ConstructorParametersRespectDefaultValueTest_Attrbutes() + { + var testObject = JsonConvert.DeserializeObject("{'Parameter2':'value!'}", new JsonSerializerSettings() + { + DefaultValueHandling = DefaultValueHandling.Populate + }); + + Assert.AreEqual("parameter1_default", testObject.Parameter1); + Assert.AreEqual("value!", testObject.Parameter2); + Assert.AreEqual("parameter3_default", testObject.Parameter3); + Assert.AreEqual("parameter4_default", testObject.Parameter4); + } + + [Test] + public void ConstructorParametersRespectDefaultValueTest() + { + var testObject = JsonConvert.DeserializeObject("{}", new JsonSerializerSettings() { ContractResolver = ConstructorParameterDefaultStringValueContractResolver.Instance }); + + Assert.AreEqual("Default Value", testObject.Parameter1); + Assert.AreEqual("Default Value", testObject.Parameter2); + } + + public class ConstructorParametersRespectDefaultValue + { + public const string DefaultValue = "Default Value"; + + public string Parameter1 { get; private set; } + public string Parameter2 { get; private set; } + + public ConstructorParametersRespectDefaultValue(string parameter1, string parameter2) + { + Parameter1 = parameter1; + Parameter2 = parameter2; + } + } + + public class ConstructorParameterDefaultStringValueContractResolver : DefaultContractResolver + { + public static new ConstructorParameterDefaultStringValueContractResolver Instance = new ConstructorParameterDefaultStringValueContractResolver(); + + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + var properties = base.CreateProperties(type, memberSerialization); + + foreach (var property in properties.Where(p => p.PropertyType == typeof(string))) + { + property.DefaultValue = ConstructorParametersRespectDefaultValue.DefaultValue; + property.DefaultValueHandling = DefaultValueHandling.Populate; + } + + return properties; + } + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs index 7e4bb842..53611cea 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs @@ -309,17 +309,20 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, DateTimeKind.Utc), c[2]); Assert.AreEqual(3, errors.Count); -#if !(NET20 || NET35) var possibleErrs = new [] { +#if !(NET20 || NET35) "[1] - 1 - The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.", "[1] - 1 - String was not recognized as a valid DateTime." +#else + // handle typo fix in later versions of .NET + "[1] - 1 - The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.", + "[1] - 1 - The string was not recognized as a valid DateTime. There is a unknown word starting at index 0." +#endif }; Assert.IsTrue(possibleErrs.Any (m => m == errors[0]), "Expected One of: " + string.Join (Environment.NewLine, possibleErrs) + Environment.NewLine + "But was: " + errors[0]); -#else - Assert.AreEqual("[1] - 1 - The string was not recognized as a valid DateTime. There is a unknown word starting at index 0.", errors[0]); -#endif + Assert.AreEqual("[2] - 2 - Unexpected token parsing date. Expected String, got StartArray. Path '[2]', line 4, position 10.", errors[1]); Assert.AreEqual("[4] - 4 - Cannot convert null value to System.DateTime. Path '[4]', line 8, position 13.", errors[2]); } diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 216b9d9f..e6cbf0a8 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -92,13 +92,13 @@ - + diff --git a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs index 5eb0cb84..de6facb9 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs @@ -155,7 +155,7 @@ namespace Newtonsoft.Json.Serialization { foreach (JsonProperty property in Properties) { - if (property.Required != Required.Default || ((property.DefaultValueHandling & DefaultValueHandling.Populate) == DefaultValueHandling.Populate) && property.Writable) + if (property.Required != Required.Default || (property.DefaultValueHandling & DefaultValueHandling.Populate) == DefaultValueHandling.Populate) { _hasRequiredOrDefaultValueProperties = true; break; diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 220362e6..455e1597 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -1662,6 +1662,34 @@ namespace Newtonsoft.Json.Serialization } } + // handle giving default values to creator parameters + // this needs to happen before the call to creator + if (propertiesPresence != null) + { + foreach (KeyValuePair propertyPresence in propertiesPresence) + { + JsonProperty property = propertyPresence.Key; + PropertyPresence presence = propertyPresence.Value; + + if (!property.Ignored && (presence == PropertyPresence.None || presence == PropertyPresence.Null)) + { + if (property.PropertyContract == null) + property.PropertyContract = GetContractSafe(property.PropertyType); + + if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling), DefaultValueHandling.Populate)) + { + // have to use the name because property presence doesn't use creator properties + int i = contract.CreatorParameters.IndexOf(p => p.PropertyName == property.PropertyName); + + if (i != -1) + { + creatorParameterValues[i] = EnsureType(reader, property.GetResolvedDefaultValue(), CultureInfo.InvariantCulture, property.PropertyContract, property.PropertyType); + } + } + } + } + } + object createdObject = creator(creatorParameterValues); if (id != null)