diff options
author | JamesNK <james@newtonking.com> | 2011-01-15 03:44:26 +0300 |
---|---|---|
committer | JamesNK <james@newtonking.com> | 2011-01-15 03:44:26 +0300 |
commit | 35351737167c27eca08de2e0114edb641e33930c (patch) | |
tree | b836f99e136e650313527fd25fb47497630d0e71 | |
parent | 5d062cc8f8754a33121198462883ebb3e82de3d0 (diff) |
-Added JsonConstructorAttribute to explicitly define which constructor to use during deserialization
-Added IFormattable to JValue
-Changed JValue ToString to call ToString on the internal value
-Fixed code emit error when deserializing interfaces in certain situations
18 files changed, 347 insertions, 32 deletions
diff --git a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs index 629d6e8..02b2331 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 17f27da..0550ca8 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
{
@@ -93,6 +94,30 @@ namespace Newtonsoft.Json.Tests.Linq }
[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 fcaf7f0..ec8d63d 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 de48b15..d671449 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 563ef62..e1938df 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs @@ -39,9 +39,59 @@ 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()
{
Book book = new Book
@@ -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 176fd8e..66445ab 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<NonPublicConstructorWithJsonConstructor>("{}");
+ 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<PublicConstructorOverridenByJsonConstructor>("{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<MultipleParamatrizedConstructorsJsonConstructor>("{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 0000000..cc0d6e6 --- /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
+{
+ /// <summary>
+ /// Instructs the <see cref="JsonSerializer"/> not to serialize the public field or public read/write property value.
+ /// </summary>
+ [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 5bace6d..cb2435a 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 554acd4..8bbb321 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -40,7 +40,7 @@ namespace Newtonsoft.Json.Linq /// <summary>
/// Represents a value in JSON (string, integer, date, etc).
/// </summary>
- public class JValue : JToken, IEquatable<JValue>
+ public class JValue : JToken, IEquatable<JValue>, IFormattable
{
private JTokenType _valueType;
private object _value;
@@ -406,6 +406,40 @@ namespace Newtonsoft.Json.Linq return _value.GetHashCode();
}
+ /// <summary>
+ /// Returns a <see cref="System.String"/> that represents this instance.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="System.String"/> that represents this instance.
+ /// </returns>
+ public override string ToString()
+ {
+ if (_value == null)
+ return string.Empty;
+
+ return _value.ToString();
+ }
+
+ /// <summary>
+ /// Returns a <see cref="System.String"/> that represents this instance.
+ /// </summary>
+ /// <param name="format">The format.</param>
+ /// <param name="formatProvider">The format provider.</param>
+ /// <returns>
+ /// A <see cref="System.String"/> that represents this instance.
+ /// </returns>
+ 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)
/// <summary>
/// Returns the <see cref="T:System.Dynamic.DynamicMetaObject"/> 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 97c213d..d3a8d9e 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -95,6 +95,7 @@ <Compile Include="Converters\KeyValuePairConverter.cs" />
<Compile Include="Converters\RegexConverter.cs" />
<Compile Include="Converters\StringEnumConverter.cs" />
+ <Compile Include="JsonConstructorAttribute.cs" />
<Compile Include="Linq\ComponentModel\JPropertyDescriptor.cs" />
<Compile Include="Linq\JPath.cs" />
<Compile Include="Linq\JRaw.cs" />
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj index 1e2ba52..3c7fe56 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -102,6 +102,7 @@ <Compile Include="Converters\RegexConverter.cs" />
<Compile Include="Converters\StringEnumConverter.cs" />
<Compile Include="ConstructorHandling.cs" />
+ <Compile Include="JsonConstructorAttribute.cs" />
<Compile Include="Utilities\DynamicProxy.cs" />
<Compile Include="Linq\JPath.cs" />
<Compile Include="Linq\JRaw.cs" />
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index 3bb79fb..4a8cd1f 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -106,6 +106,7 @@ <Compile Include="DefaultValueHandling.cs" />
<Compile Include="IJsonLineInfo.cs" />
<Compile Include="JsonArrayAttribute.cs" />
+ <Compile Include="JsonConstructorAttribute.cs" />
<Compile Include="JsonContainerAttribute.cs" />
<Compile Include="JsonConvert.cs" />
<Compile Include="JsonConverter.cs" />
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj index 6604d0f..4e194ce 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj @@ -79,6 +79,7 @@ <Compile Include="DefaultValueHandling.cs" />
<Compile Include="IJsonLineInfo.cs" />
<Compile Include="JsonArrayAttribute.cs" />
+ <Compile Include="JsonConstructorAttribute.cs" />
<Compile Include="JsonContainerAttribute.cs" />
<Compile Include="JsonConvert.cs" />
<Compile Include="JsonConverter.cs" />
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index a7ca2cb..2959062 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -101,6 +101,7 @@ <Compile Include="Converters\RegexConverter.cs" />
<Compile Include="Converters\StringEnumConverter.cs" />
<Compile Include="ConstructorHandling.cs" />
+ <Compile Include="JsonConstructorAttribute.cs" />
<Compile Include="Utilities\DynamicProxy.cs" />
<Compile Include="Linq\JPath.cs" />
<Compile Include="Linq\JRaw.cs" />
diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 62c6484..cfbdb51 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<ConstructorInfo> 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<ConstructorInfo> 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<object, object> methodCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(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<object, object> shouldSerializeCall =
JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(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 e52539f..c3d4e6e 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs @@ -46,6 +46,14 @@ namespace Newtonsoft.Json.Serialization public JsonPropertyCollection Properties { get; private set; }
/// <summary>
+ /// Gets or sets the override constructor used to create the object.
+ /// This is set when a constructor is marked up using the
+ /// JsonConstructor attribute.
+ /// </summary>
+ /// <value>The override constructor.</value>
+ public ConstructorInfo OverrideConstructor { get; set; }
+
+ /// <summary>
/// Gets or sets the parametrized constructor used to create the object.
/// </summary>
/// <value>The parametrized constructor.</value>
diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 2a8e494..5747d9e 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<JsonProperty, object> 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<ParameterInfo, object> constructorParameters = contract.ParametrizedConstructor.GetParameters().ToDictionary(p => p, p => (object)null);
+ 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)
@@ -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 e1e6b91..eab9db7 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;
}
|