diff --git a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs index 629d6e88..02b23311 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs @@ -7,6 +7,7 @@ using System.Text; using Newtonsoft.Json.Linq; using NUnit.Framework; using Newtonsoft.Json.Utilities; +using System.Globalization; namespace Newtonsoft.Json.Tests.Linq { @@ -106,6 +107,56 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual("blah blah", (string)v); } + //[Test] + //public void JValueEquals() + //{ + // JObject o = new JObject( + // new JProperty("Null", new JValue(null, JTokenType.Null)), + // new JProperty("Integer", new JValue(1)), + // new JProperty("Float", new JValue(1.1)), + // new JProperty("DateTime", new JValue(new DateTime(2000, 12, 29, 23, 51, 10, DateTimeKind.Utc))), + // new JProperty("Boolean", new JValue(true)), + // new JProperty("String", new JValue("A string lol!")), + // new JProperty("Bytes", new JValue(Encoding.UTF8.GetBytes("A string lol!"))) + // ); + + // dynamic d = o; + + // Assert.IsTrue(d.Null == null); + // Assert.IsTrue(d.Null == new JValue(null, JTokenType.Null)); + + // //if (d.NullValue == null) + // // Console.WriteLine("Null"); + // //else + // // Console.WriteLine("Not null"); + + // //Console.WriteLine(d.IntValue == 1); + //} + + [Test] + public void JValueToString() + { + JObject o = new JObject( + new JProperty("Null", new JValue(null, JTokenType.Null)), + new JProperty("Integer", new JValue(1)), + new JProperty("Float", new JValue(1.1)), + new JProperty("DateTime", new JValue(new DateTime(2000, 12, 29, 23, 51, 10, DateTimeKind.Utc))), + new JProperty("Boolean", new JValue(true)), + new JProperty("String", new JValue("A string lol!")), + new JProperty("Bytes", new JValue(Encoding.UTF8.GetBytes("A string lol!"))) + ); + + dynamic d = o; + + Assert.AreEqual("", d.Null.ToString()); + Assert.AreEqual("1", d.Integer.ToString()); + Assert.AreEqual("1.1", d.Float.ToString()); + Assert.AreEqual("12/29/2000 23:51:10", d.DateTime.ToString(null, CultureInfo.InvariantCulture)); + Assert.AreEqual("True", d.Boolean.ToString()); + Assert.AreEqual("A string lol!", d.String.ToString()); + Assert.AreEqual("System.Byte[]", d.Bytes.ToString()); + } + [Test] public void JObjectGetDynamicPropertyNames() { diff --git a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs index 17f27da7..0550ca8c 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs @@ -29,6 +29,7 @@ using System.Linq; using System.Text; using NUnit.Framework; using Newtonsoft.Json.Linq; +using System.Globalization; namespace Newtonsoft.Json.Tests.Linq { @@ -92,6 +93,30 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(JTokenType.String, stringValue.Type); } + [Test] + public void ToString() + { + JValue v; + + v = new JValue(true); + Assert.AreEqual("True", v.ToString()); + + v = new JValue(Encoding.UTF8.GetBytes("Blah")); + Assert.AreEqual("System.Byte[]", v.ToString(null, CultureInfo.InvariantCulture)); + + v = new JValue("I am a string!"); + Assert.AreEqual("I am a string!", v.ToString()); + + v = new JValue(null, JTokenType.Null); + Assert.AreEqual("", v.ToString()); + + v = new JValue(null, JTokenType.Null); + Assert.AreEqual("", v.ToString(null, CultureInfo.InvariantCulture)); + + v = new JValue(new DateTime(2000, 12, 12, 20, 59, 59, DateTimeKind.Utc), JTokenType.Date); + Assert.AreEqual("12/12/2000 20:59:59", v.ToString(null, CultureInfo.InvariantCulture)); + } + [Test] [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot access child value on Newtonsoft.Json.Linq.JValue.")] public void Last() diff --git a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs index fcaf7f00..ec8d63d3 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs @@ -287,7 +287,7 @@ keyword such as type of business."" Assert.AreEqual(@"""Width"": 1.1", o.Property("Width").ToString()); Assert.AreEqual(@"1.1", o.Property("Width").Value.ToString()); Assert.AreEqual(@"""Open"": false", o.Property("Open").ToString()); - Assert.AreEqual(@"false", o.Property("Open").Value.ToString()); + Assert.AreEqual(@"False", o.Property("Open").Value.ToString()); json = @"[null,undefined]"; @@ -296,8 +296,8 @@ keyword such as type of business."" null, undefined ]", a.ToString()); - Assert.AreEqual(@"null", a.Children().ElementAt(0).ToString()); - Assert.AreEqual(@"undefined", a.Children().ElementAt(1).ToString()); + Assert.AreEqual(@"", a.Children().ElementAt(0).ToString()); + Assert.AreEqual(@"", a.Children().ElementAt(1).ToString()); } [Test] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs index de48b15e..d6714493 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs @@ -10,7 +10,7 @@ namespace Newtonsoft.Json.Tests.Serialization public class ConstructorHandlingTests : TestFixtureBase { [Test] - [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Unable to find a constructor to use for type Newtonsoft.Json.Tests.TestObjects.PrivateConstructorTestClass. A class should either have a default constructor or only one constructor with arguments.")] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Unable to find a constructor to use for type Newtonsoft.Json.Tests.TestObjects.PrivateConstructorTestClass. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.")] public void FailWithPrivateConstructorAndDefault() { string json = @"{Name:""Name!""}"; diff --git a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs index 563ef620..e1938df1 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs @@ -39,8 +39,58 @@ namespace Newtonsoft.Json.Tests.Serialization public string AuthorCountry { get; set; } } + public interface IPerson + { + string FirstName { get; set; } + string LastName { get; set; } + DateTime BirthDate { get; set; } + } + + public class Employee : IPerson + { + public string FirstName { get; set; } + public string LastName { get; set; } + public DateTime BirthDate { get; set; } + + public string Department { get; set; } + public string JobTitle { get; set; } + } + + public class IPersonContractResolver : DefaultContractResolver + { + protected override JsonContract CreateContract(Type objectType) + { + if (objectType == typeof(Employee)) + objectType = typeof(IPerson); + + return base.CreateContract(objectType); + } + } + public class ContractResolverTests : TestFixtureBase { + [Test] + public void SerializeInterface() + { + Employee employee = new Employee + { + BirthDate = new DateTime(1977, 12, 30, 1, 1, 1, DateTimeKind.Utc), + FirstName = "Maurice", + LastName = "Moss", + Department = "IT", + JobTitle = "Support" + }; + + string iPersonJson = JsonConvert.SerializeObject(employee, Formatting.Indented, + new JsonSerializerSettings { ContractResolver = new IPersonContractResolver() }); + + Assert.AreEqual(@"{ + ""FirstName"": ""Maurice"", + ""LastName"": ""Moss"", + ""BirthDate"": ""\/Date(252291661000)\/"" +}", iPersonJson); + } + [Test] public void SingleTypeWithMultipleContractResolvers() { @@ -127,4 +177,4 @@ namespace Newtonsoft.Json.Tests.Serialization }", includeCompilerGeneratedJson); } } -} +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 176fd8e1..66445abb 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -1802,7 +1802,7 @@ keyword such as type of business."" #endif [Test] - [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Unable to find a constructor to use for type Newtonsoft.Json.Tests.TestObjects.Event. A class should either have a default constructor or only one constructor with arguments.")] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Unable to find a constructor to use for type Newtonsoft.Json.Tests.TestObjects.Event. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.")] public void FailWhenClassWithNoDefaultConstructorHasMultipleConstructorsWithArguments() { string json = @"{""sublocation"":""AlertEmailSender.Program.Main"",""userId"":0,""type"":0,""summary"":""Loading settings variables"",""details"":null,""stackTrace"":"" at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)\r\n at System.Environment.get_StackTrace()\r\n at mr.Logging.Event..ctor(String summary) in C:\\Projects\\MRUtils\\Logging\\Event.vb:line 71\r\n at AlertEmailSender.Program.Main(String[] args) in C:\\Projects\\AlertEmailSender\\AlertEmailSender\\Program.cs:line 25"",""tag"":null,""time"":""\/Date(1249591032026-0400)\/""}"; @@ -4043,5 +4043,86 @@ keyword such as type of business."" Assert.AreEqual(decimal.MinValue, obj.Value); } + + public class NonPublicConstructorWithJsonConstructor + { + public string Value { get; private set; } + public string Constructor { get; private set; } + + [JsonConstructor] + private NonPublicConstructorWithJsonConstructor() + { + Constructor = "NonPublic"; + } + + public NonPublicConstructorWithJsonConstructor(string value) + { + Value = value; + Constructor = "Public Paramatized"; + } + } + + [Test] + public void NonPublicConstructorWithJsonConstructorTest() + { + NonPublicConstructorWithJsonConstructor c = JsonConvert.DeserializeObject("{}"); + Assert.AreEqual("NonPublic", c.Constructor); + } + + public class PublicConstructorOverridenByJsonConstructor + { + public string Value { get; private set; } + public string Constructor { get; private set; } + + public PublicConstructorOverridenByJsonConstructor() + { + Constructor = "NonPublic"; + } + + [JsonConstructor] + public PublicConstructorOverridenByJsonConstructor(string value) + { + Value = value; + Constructor = "Public Paramatized"; + } + } + + [Test] + public void PublicConstructorOverridenByJsonConstructorTest() + { + PublicConstructorOverridenByJsonConstructor c = JsonConvert.DeserializeObject("{Value:'value!'}"); + Assert.AreEqual("Public Paramatized", c.Constructor); + Assert.AreEqual("value!", c.Value); + } + + public class MultipleParamatrizedConstructorsJsonConstructor + { + public string Value { get; private set; } + public int Age { get; private set; } + public string Constructor { get; private set; } + + public MultipleParamatrizedConstructorsJsonConstructor(string value) + { + Value = value; + Constructor = "Public Paramatized 1"; + } + + [JsonConstructor] + public MultipleParamatrizedConstructorsJsonConstructor(string value, int age) + { + Value = value; + Age = age; + Constructor = "Public Paramatized 2"; + } + } + + [Test] + public void MultipleParamatrizedConstructorsJsonConstructorTest() + { + MultipleParamatrizedConstructorsJsonConstructor c = JsonConvert.DeserializeObject("{Value:'value!', Age:1}"); + Assert.AreEqual("Public Paramatized 2", c.Constructor); + Assert.AreEqual("value!", c.Value); + Assert.AreEqual(1, c.Age); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonConstructorAttribute.cs b/Src/Newtonsoft.Json/JsonConstructorAttribute.cs new file mode 100644 index 00000000..cc0d6e6d --- /dev/null +++ b/Src/Newtonsoft.Json/JsonConstructorAttribute.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the not to serialize the public field or public read/write property value. + /// + [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false)] + public sealed class JsonConstructorAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonValidatingReader.cs b/Src/Newtonsoft.Json/JsonValidatingReader.cs index 5bace6db..cb2435a0 100644 --- a/Src/Newtonsoft.Json/JsonValidatingReader.cs +++ b/Src/Newtonsoft.Json/JsonValidatingReader.cs @@ -32,6 +32,7 @@ using Newtonsoft.Json.Schema; using Newtonsoft.Json.Utilities; using System.Globalization; using System.Text.RegularExpressions; +using System.IO; namespace Newtonsoft.Json { @@ -300,8 +301,11 @@ namespace Newtonsoft.Json if (schema.Enum != null) { + StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); + value.WriteTo(new JsonTextWriter(sw)); + if (!schema.Enum.ContainsValue(value, new JTokenEqualityComparer())) - RaiseError("Value {0} is not defined in enum.".FormatWith(CultureInfo.InvariantCulture, value), + RaiseError("Value {0} is not defined in enum.".FormatWith(CultureInfo.InvariantCulture, sw.ToString()), schema); } diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index 554acd48..8bbb3216 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -40,7 +40,7 @@ namespace Newtonsoft.Json.Linq /// /// Represents a value in JSON (string, integer, date, etc). /// - public class JValue : JToken, IEquatable + public class JValue : JToken, IEquatable, IFormattable { private JTokenType _valueType; private object _value; @@ -406,6 +406,40 @@ namespace Newtonsoft.Json.Linq return _value.GetHashCode(); } + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + if (_value == null) + return string.Empty; + + return _value.ToString(); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (_value == null) + return string.Empty; + + IFormattable formattable = _value as IFormattable; + if (formattable != null) + return formattable.ToString(format, formatProvider); + else + return _value.ToString(); + } + #if !(NET35 || NET20 || WINDOWS_PHONE) /// /// Returns the responsible for binding operations performed on this object. @@ -440,6 +474,26 @@ namespace Newtonsoft.Json.Linq result = ConvertUtils.Convert(instance.Value, CultureInfo.InvariantCulture, binder.Type); return true; } + + //public override bool TryBinaryOperation(JValue instance, BinaryOperationBinder binder, object arg, out object result) + //{ + // dynamic d1 = arg; + // dynamic d2 = instance._value; + // //// use built in JValue equals + // //if (arg is JValue) + // //{ + // // result = null; + // // return false; + // //} + + // //if (binder.Operation == ExpressionType.Equal) + // //{ + // // result = Compare(instance.Type, instance.Value, arg); + // // return true; + // //} + + // //return base.TryBinaryOperation(instance, binder, arg, out result); + //} } #endif } diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index 97c213d8..d3a8d9e0 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -95,6 +95,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj index 1e2ba528..3c7fe56b 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -102,6 +102,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index 3bb79fb2..4a8cd1f4 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -106,6 +106,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj index 6604d0fe..4e194ce6 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj @@ -79,6 +79,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index a7ca2cbf..29590626 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -101,6 +101,7 @@ + diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 62c64846..cfbdb510 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -64,7 +64,7 @@ namespace Newtonsoft.Json.Serialization if (!(obj is ResolverContractKey)) return false; - return Equals((ResolverContractKey) obj); + return Equals((ResolverContractKey)obj); } public bool Equals(ResolverContractKey other) @@ -277,17 +277,31 @@ namespace Newtonsoft.Json.Serialization contract.MemberSerialization = JsonTypeReflector.GetObjectMemberSerialization(objectType); contract.Properties.AddRange(CreateProperties(contract.UnderlyingType, contract.MemberSerialization)); - if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic) + 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); return contract; } + private ConstructorInfo GetAttributeConstructor(Type objectType) + { + IList markedConstructors = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(c => c.IsDefined(typeof(JsonConstructorAttribute), true)).ToList(); + + if (markedConstructors.Count > 1) + throw new Exception("Multiple constructors with the JsonConstructorAttribute."); + else if (markedConstructors.Count == 1) + return markedConstructors[0]; + + return null; + } + private ConstructorInfo GetParametrizedConstructor(Type objectType) { - ConstructorInfo[] constructors = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance); + IList constructors = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance); - if (constructors.Length == 1) + if (constructors.Count == 1) return constructors[0]; else return null; @@ -309,7 +323,7 @@ namespace Newtonsoft.Json.Serialization } #if !PocketPC && !NET20 - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Runtime.Serialization.DataContractAttribute.#get_IsReference()")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Runtime.Serialization.DataContractAttribute.#get_IsReference()")] #endif private void InitializeContract(JsonContract contract) { @@ -412,7 +426,7 @@ namespace Newtonsoft.Json.Serialization { JsonPrimitiveContract contract = new JsonPrimitiveContract(objectType); InitializeContract(contract); - + return contract; } @@ -440,7 +454,7 @@ namespace Newtonsoft.Json.Serialization JsonISerializableContract contract = new JsonISerializableContract(objectType); InitializeContract(contract); - ConstructorInfo constructorInfo = objectType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new [] {typeof (SerializationInfo), typeof (StreamingContext)}, null); + ConstructorInfo constructorInfo = objectType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(SerializationInfo), typeof(StreamingContext) }, null); if (constructorInfo != null) { MethodCall methodCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(constructorInfo); @@ -574,7 +588,7 @@ namespace Newtonsoft.Json.Serialization if (attributeType == typeof(OnErrorAttribute)) { if (parameters == null || parameters.Length != 2 || parameters[0].ParameterType != typeof(StreamingContext) || parameters[1].ParameterType != typeof(ErrorContext)) - throw new Exception("Serialization Error Callback '{1}' in type '{0}' must have two parameters of type '{2}' and '{3}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method, typeof (StreamingContext), typeof(ErrorContext))); + throw new Exception("Serialization Error Callback '{1}' in type '{0}' must have two parameters of type '{2}' and '{3}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method, typeof(StreamingContext), typeof(ErrorContext))); } else { @@ -646,7 +660,7 @@ namespace Newtonsoft.Json.Serialization JsonProperty property = new JsonProperty(); 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); @@ -689,7 +703,7 @@ namespace Newtonsoft.Json.Serialization (memberSerialization == MemberSerialization.OptIn && propertyAttribute == null #if !PocketPC && !NET20 - && dataMemberAttribute == null + && dataMemberAttribute == null #endif )); @@ -735,7 +749,7 @@ namespace Newtonsoft.Json.Serialization MethodCall shouldSerializeCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(shouldSerializeMethod); - return o => (bool) shouldSerializeCall(o); + return o => (bool)shouldSerializeCall(o); } private void SetIsSpecifiedActions(JsonProperty property, MemberInfo member) diff --git a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs index e52539f9..c3d4e6ef 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs @@ -45,6 +45,14 @@ namespace Newtonsoft.Json.Serialization /// The object's properties. public JsonPropertyCollection Properties { get; private set; } + /// + /// Gets or sets the override constructor used to create the object. + /// This is set when a constructor is marked up using the + /// JsonConstructor attribute. + /// + /// The override constructor. + public ConstructorInfo OverrideConstructor { get; set; } + /// /// Gets or sets the parametrized constructor used to create the object. /// diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 2a8e4949..5747d9e9 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -831,27 +831,35 @@ namespace Newtonsoft.Json.Serialization if (contract.UnderlyingType.IsInterface || contract.UnderlyingType.IsAbstract) throw new JsonSerializationException("Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); - if (contract.DefaultCreator != null && + if (contract.OverrideConstructor != null) + { + if (contract.OverrideConstructor.GetParameters().Length > 0) + return CreateObjectFromNonDefaultConstructor(reader, contract, contract.OverrideConstructor, id); + + newObject = contract.OverrideConstructor.Invoke(null); + } + else if (contract.DefaultCreator != null && (!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) { newObject = contract.DefaultCreator(); } - - if (newObject != null) + else if (contract.ParametrizedConstructor != null) { - PopulateObject(newObject, reader, contract, id); - return newObject; + return CreateObjectFromNonDefaultConstructor(reader, contract, contract.ParametrizedConstructor, id); } - return CreateObjectFromNonDefaultConstructor(reader, contract, id); + if (newObject == null) + throw new JsonSerializationException("Unable to find a constructor to use for type {0}. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + PopulateObject(newObject, reader, contract, id); + return newObject; } - private object CreateObjectFromNonDefaultConstructor(JsonReader reader, JsonObjectContract contract, string id) + private object CreateObjectFromNonDefaultConstructor(JsonReader reader, JsonObjectContract contract, ConstructorInfo constructorInfo, string id) { - Type objectType = contract.UnderlyingType; + ValidationUtils.ArgumentNotNull(constructorInfo, "constructorInfo"); - if (contract.ParametrizedConstructor == null) - throw new JsonSerializationException("Unable to find a constructor to use for type {0}. A class should either have a default constructor or only one constructor with arguments.".FormatWith(CultureInfo.InvariantCulture, objectType)); + Type objectType = contract.UnderlyingType; // create a dictionary to put retrieved values into IDictionary propertyValues = contract.Properties.Where(p => !p.Ignored).ToDictionary(kv => kv, kv => (object)null); @@ -897,7 +905,7 @@ namespace Newtonsoft.Json.Serialization } } while (!exit && reader.Read()); - IDictionary constructorParameters = contract.ParametrizedConstructor.GetParameters().ToDictionary(p => p, p => (object)null); + IDictionary constructorParameters = constructorInfo.GetParameters().ToDictionary(p => p, p => (object)null); IDictionary remainingPropertyValues = new Dictionary(); foreach (KeyValuePair propertyValue in propertyValues) @@ -909,7 +917,7 @@ namespace Newtonsoft.Json.Serialization remainingPropertyValues.Add(propertyValue); } - object createdObject = contract.ParametrizedConstructor.Invoke(constructorParameters.Values.ToArray()); + object createdObject = constructorInfo.Invoke(constructorParameters.Values.ToArray()); if (id != null) Serializer.ReferenceResolver.AddReference(id, createdObject); diff --git a/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs b/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs index e1e6b919..eab9db7b 100644 --- a/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs +++ b/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs @@ -42,7 +42,7 @@ namespace Newtonsoft.Json.Utilities { DynamicMethod dynamicMethod = !owner.IsInterface ? new DynamicMethod(name, returnType, parameterTypes, owner, true) - : new DynamicMethod(name, returnType, parameterTypes, (Module)null, true); + : new DynamicMethod(name, returnType, parameterTypes, owner.Module, true); return dynamicMethod; }