-More object creation refactoring

This commit is contained in:
James Newton-King
2014-05-07 15:24:42 +12:00
parent 3e4eb24047
commit cf939ffc29
10 changed files with 317 additions and 162 deletions
@@ -75,4 +75,4 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.3.17302")]
[assembly: AssemblyFileVersion("6.0.3.17307")]
@@ -23,6 +23,7 @@
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Reflection;
using Newtonsoft.Json.Tests.TestObjects;
#if !NETFX_CORE
@@ -66,7 +67,7 @@ namespace Newtonsoft.Json.Tests.Serialization
[Test]
public void FailWithPrivateConstructorPlusParametizedAndDefault()
{
ExceptionAssert.Throws<TargetInvocationException>(
ExceptionAssert.Throws<Exception>(
null,
() =>
{
@@ -150,6 +150,74 @@ namespace Newtonsoft.Json.Tests.Serialization
Assert.IsFalse(contract.Properties.Any(c => c.PropertyName == "InvariantInfo"));
}
[Test]
public void ParametrizedCreator()
{
var resolver = new DefaultContractResolver();
var contract = (JsonObjectContract)resolver.ResolveContract(typeof(PublicParametizedConstructorWithPropertyNameConflictWithAttribute));
Assert.IsNull(contract.DefaultCreator);
Assert.IsNotNull(contract.ParametrizedCreator);
#pragma warning disable 618
Assert.AreEqual(contract.ParametrizedConstructor, typeof(PublicParametizedConstructorWithPropertyNameConflictWithAttribute).GetConstructor(new[] { typeof(string) }));
#pragma warning restore 618
Assert.AreEqual(1, contract.CreatorParameters.Count);
Assert.AreEqual("name", contract.CreatorParameters[0].PropertyName);
#pragma warning disable 618
contract.ParametrizedConstructor = null;
#pragma warning restore 618
Assert.IsNull(contract.ParametrizedCreator);
}
[Test]
public void OverrideCreator()
{
var resolver = new DefaultContractResolver();
var contract = (JsonObjectContract)resolver.ResolveContract(typeof(MultipleParamatrizedConstructorsJsonConstructor));
Assert.IsNull(contract.DefaultCreator);
Assert.IsNotNull(contract.OverrideCreator);
#pragma warning disable 618
Assert.AreEqual(contract.OverrideConstructor, typeof(MultipleParamatrizedConstructorsJsonConstructor).GetConstructor(new[] { typeof(string), typeof(int) }));
#pragma warning restore 618
Assert.AreEqual(2, contract.CreatorParameters.Count);
Assert.AreEqual("Value", contract.CreatorParameters[0].PropertyName);
Assert.AreEqual("Age", contract.CreatorParameters[1].PropertyName);
#pragma warning disable 618
contract.OverrideConstructor = null;
#pragma warning restore 618
Assert.IsNull(contract.OverrideCreator);
}
[Test]
public void CustomOverrideCreator()
{
var resolver = new DefaultContractResolver();
var contract = (JsonObjectContract)resolver.ResolveContract(typeof(MultipleParamatrizedConstructorsJsonConstructor));
bool ensureCustomCreatorCalled = false;
contract.OverrideCreator = args =>
{
ensureCustomCreatorCalled = true;
return new MultipleParamatrizedConstructorsJsonConstructor((string) args[0], (int) args[1]);
};
#pragma warning disable 618
Assert.IsNull(contract.OverrideConstructor);
#pragma warning restore 618
var o = JsonConvert.DeserializeObject<MultipleParamatrizedConstructorsJsonConstructor>("{Value:'value!', Age:1}", new JsonSerializerSettings
{
ContractResolver = resolver
});
Assert.AreEqual("value!", o.Value);
Assert.AreEqual(1, o.Age);
Assert.IsTrue(ensureCustomCreatorCalled);
}
[Test]
public void SerializeInterface()
{
@@ -4952,27 +4952,6 @@ To fix this error either change the environment to be fully trusted, change the
Assert.IsTrue(deserializedResponse.Data.DeepEquals(response.Data));
}
public abstract class Test<T>
{
public abstract T Value { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class DecimalTest : Test<decimal>
{
protected DecimalTest()
{
}
public DecimalTest(decimal val)
{
Value = val;
}
[JsonProperty]
public override decimal Value { get; set; }
}
[Test]
public void DeserializeMinValueDecimal()
{
@@ -4983,24 +4962,6 @@ To fix this error either change the environment to be fully trusted, change the
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()
{
@@ -5008,24 +4969,6 @@ To fix this error either change the environment to be fully trusted, change the
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()
{
@@ -5034,27 +4977,6 @@ To fix this error either change the environment to be fully trusted, change the
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()
{
@@ -5064,11 +4986,6 @@ To fix this error either change the environment to be fully trusted, change the
Assert.AreEqual(1, c.Age);
}
public class EnumerableClass
{
public IEnumerable<string> Enumerable { get; set; }
}
[Test]
public void DeserializeEnumerable()
{
@@ -5094,18 +5011,6 @@ To fix this error either change the environment to be fully trusted, change the
Assert.AreEqual("Three", c2.Enumerable.ElementAt(2));
}
[JsonObject(MemberSerialization.OptIn)]
public class ItemBase
{
[JsonProperty]
public string Name { get; set; }
}
public class ComplexItem : ItemBase
{
public Stream Source { get; set; }
}
[Test]
public void SerializeAttributesOnBase()
{
@@ -5118,14 +5023,6 @@ To fix this error either change the environment to be fully trusted, change the
}", json);
}
public class DeserializeStringConvert
{
public string Name { get; set; }
public int Age { get; set; }
public double Height { get; set; }
public decimal Price { get; set; }
}
[Test]
public void DeserializeStringEnglish()
{
@@ -7961,4 +7858,107 @@ Parameter name: value",
}
#endif
}
public abstract class Test<T>
{
public abstract T Value { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class DecimalTest : Test<decimal>
{
protected DecimalTest()
{
}
public DecimalTest(decimal val)
{
Value = val;
}
[JsonProperty]
public override decimal Value { get; set; }
}
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";
}
}
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";
}
}
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";
}
}
public class EnumerableClass
{
public IEnumerable<string> Enumerable { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class ItemBase
{
[JsonProperty]
public string Name { get; set; }
}
public class ComplexItem : ItemBase
{
public Stream Source { get; set; }
}
public class DeserializeStringConvert
{
public string Name { get; set; }
public int Age { get; set; }
public double Height { get; set; }
public decimal Price { get; set; }
}
}
@@ -279,7 +279,7 @@ Newtonsoft.Json Error: 0 : Error!
Assert.IsTrue(traceWriter.TraceRecords[2].Message.StartsWith("Finished deserializing System.Collections.Generic.IList`1[System.Int32]. Path 'IntList'"));
Assert.AreEqual("Started deserializing System.String[]. Path 'StringArray', line 6, position 19.", traceWriter.TraceRecords[3].Message);
Assert.IsTrue(traceWriter.TraceRecords[4].Message.StartsWith("Finished deserializing System.String[]. Path 'StringArray'"));
Assert.AreEqual("Deserializing System.Version using a non-default constructor 'Void .ctor(Int32, Int32, Int32, Int32)'. Path 'Version.Major', line 11, position 13.", traceWriter.TraceRecords[5].Message);
Assert.AreEqual("Deserializing System.Version using creator with parameters: Major, Minor, Build, Revision. Path 'Version.Major', line 11, position 13.", traceWriter.TraceRecords[5].Message);
Assert.IsTrue(traceWriter.TraceRecords[6].Message.StartsWith("Started deserializing System.Version. Path 'Version'"));
Assert.IsTrue(traceWriter.TraceRecords[7].Message.StartsWith("Finished deserializing System.Version. Path 'Version'"));
Assert.AreEqual("Started deserializing System.Collections.Generic.IDictionary`2[System.String,System.String]. Path 'StringDictionary.1', line 19, position 9.", traceWriter.TraceRecords[8].Message);
@@ -533,7 +533,7 @@ Newtonsoft.Json Error: 0 : Error!
Assert.AreEqual("Started deserializing System.Collections.Generic.Dictionary`2[System.String,System.String]. Path '$values[0].key!', line 6, position 14.", traceWriter.TraceRecords[3].Message);
Assert.IsTrue(traceWriter.TraceRecords[4].Message.StartsWith("Finished deserializing System.Collections.Generic.Dictionary`2[System.String,System.String]. Path '$values[0]'"));
Assert.AreEqual("Resolved type 'System.Version, mscorlib' to System.Version. Path '$values[1].$type', line 9, position 42.", traceWriter.TraceRecords[5].Message);
Assert.AreEqual("Deserializing System.Version using a custom method 'Void .ctor(Int32, Int32, Int32, Int32)'. Path '$values[1].Major', line 10, position 15.", traceWriter.TraceRecords[6].Message);
Assert.AreEqual("Deserializing System.Version using creator with parameters: Major, Minor, Build, Revision. Path '$values[1].Major', line 10, position 15.", traceWriter.TraceRecords[6].Message);
Assert.IsTrue(traceWriter.TraceRecords[7].Message.StartsWith("Started deserializing System.Version. Path '$values[1]'"));
Assert.IsTrue(traceWriter.TraceRecords[8].Message.StartsWith("Finished deserializing System.Version. Path '$values[1]'"));
Assert.IsTrue(traceWriter.TraceRecords[9].Message.StartsWith("Finished deserializing System.Collections.Generic.List`1[System.Object]. Path '$values'"));
@@ -610,12 +610,33 @@ Newtonsoft.Json Error: 0 : Error!
TraceWriter = traceWriter
});
Assert.AreEqual("Deserializing System.Version using a custom method 'Void .ctor(Int32, Int32, Int32, Int32)'. Path 'Major', line 2, position 11.", traceWriter.TraceRecords[0].Message);
Assert.AreEqual("Deserializing System.Version using creator with parameters: Major, Minor, Build, Revision. Path 'Major', line 2, position 11.", traceWriter.TraceRecords[0].Message);
Assert.AreEqual("Could not find member 'MissingMemberProperty' on System.Version. Path 'MissingMemberProperty', line 8, position 32.", traceWriter.TraceRecords[1].Message);
Assert.IsTrue(traceWriter.TraceRecords[2].Message.StartsWith("Started deserializing System.Version. Path ''"));
Assert.IsTrue(traceWriter.TraceRecords[3].Message.StartsWith("Finished deserializing System.Version. Path ''"));
}
[Test]
public void PublicParametizedConstructorWithPropertyNameConflictWithAttribute()
{
InMemoryTraceWriter traceWriter = new InMemoryTraceWriter
{
LevelFilter = TraceLevel.Verbose
};
string json = @"{name:""1""}";
PublicParametizedConstructorWithPropertyNameConflictWithAttribute c = JsonConvert.DeserializeObject<PublicParametizedConstructorWithPropertyNameConflictWithAttribute>(json, new JsonSerializerSettings
{
TraceWriter = traceWriter
});
Assert.IsNotNull(c);
Assert.AreEqual(1, c.Name);
Assert.AreEqual("Deserializing Newtonsoft.Json.Tests.TestObjects.PublicParametizedConstructorWithPropertyNameConflictWithAttribute using creator with parameters: name. Path 'name', line 1, position 6.", traceWriter.TraceRecords[0].Message);
}
[Test]
public void ShouldSerializeTestClass()
{
@@ -91,5 +91,5 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.3.17302")]
[assembly: AssemblyFileVersion("6.0.3.17307")]
[assembly: CLSCompliant(true)]
@@ -380,8 +380,10 @@ namespace Newtonsoft.Json.Serialization
// check if a JsonConstructorAttribute has been defined and use that
if (overrideConstructor != null)
{
#pragma warning disable 618
contract.OverrideConstructor = overrideConstructor;
contract.ConstructorParameters.AddRange(CreateConstructorParameters(overrideConstructor, contract.Properties));
#pragma warning restore 618
contract.CreatorParameters.AddRange(CreateConstructorParameters(overrideConstructor, contract.Properties));
}
else if (contract.MemberSerialization == MemberSerialization.Fields)
{
@@ -397,8 +399,10 @@ namespace Newtonsoft.Json.Serialization
ConstructorInfo constructor = GetParametrizedConstructor(contract.NonNullableUnderlyingType);
if (constructor != null)
{
#pragma warning disable 618
contract.ParametrizedConstructor = constructor;
contract.ConstructorParameters.AddRange(CreateConstructorParameters(constructor, contract.Properties));
#pragma warning restore 618
contract.CreatorParameters.AddRange(CreateConstructorParameters(constructor, contract.Properties));
}
}
@@ -60,7 +60,16 @@ namespace Newtonsoft.Json.Serialization
/// <summary>
/// Gets the constructor parameters required for any non-default constructor
/// </summary>
public JsonPropertyCollection ConstructorParameters { get; private set; }
[Obsolete("ConstructorParameters is obsolete. Use CreatorParameters instead.")]
public JsonPropertyCollection ConstructorParameters
{
get { return CreatorParameters; }
}
/// <summary>
/// Gets a collection of <see cref="JsonProperty"/> instances that define the parameters used with <see cref="OverrideCreator"/>.
/// </summary>
public JsonPropertyCollection CreatorParameters { get; private set; }
/// <summary>
/// Gets or sets the override constructor used to create the object.
@@ -68,13 +77,23 @@ namespace Newtonsoft.Json.Serialization
/// JsonConstructor attribute.
/// </summary>
/// <value>The override constructor.</value>
[Obsolete("OverrideConstructor is obsolete. Use OverrideCreator instead.")]
public ConstructorInfo OverrideConstructor
{
get { return _overrideConstructor; }
set
{
_overrideConstructor = value;
ParametrizedCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(value);
if (value != null)
{
MethodCall<object, object> ctor = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(value);
_overrideCreator = args => ctor(null, args);
}
else
{
_overrideCreator = null;
}
}
}
@@ -82,17 +101,44 @@ namespace Newtonsoft.Json.Serialization
/// Gets or sets the parametrized constructor used to create the object.
/// </summary>
/// <value>The parametrized constructor.</value>
[Obsolete("ParametrizedConstructor is obsolete. Use OverrideCreator instead.")]
public ConstructorInfo ParametrizedConstructor
{
get { return _parametrizedConstructor; }
set
{
_parametrizedConstructor = value;
ParametrizedCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(value);
if (value != null)
{
MethodCall<object, object> ctor = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(value);
_parametrizedCreator = args => ctor(null, args);
}
else
{
_parametrizedCreator = null;
}
}
}
internal MethodCall<object, object> ParametrizedCreator { get; set; }
/// <summary>
/// Gets or sets the function used to create the object. When set this function will override <see cref="P:DefaultCreator"/>.
/// This function can be called with a collection of arguments which are defined by the <see cref="CreatorParameters"/> collection.
/// </summary>
/// <value>The function used to create the object.</value>
public Func<object[], object> OverrideCreator
{
get { return _overrideCreator; }
set
{
_overrideCreator = value;
_overrideConstructor = null;
}
}
internal Func<object[], object> ParametrizedCreator
{
get { return _parametrizedCreator; }
}
/// <summary>
/// Gets or sets the extension data setter.
@@ -107,6 +153,8 @@ namespace Newtonsoft.Json.Serialization
private bool? _hasRequiredOrDefaultValueProperties;
private ConstructorInfo _parametrizedConstructor;
private ConstructorInfo _overrideConstructor;
private Func<object[], object> _overrideCreator;
private Func<object[], object> _parametrizedCreator;
internal bool HasRequiredOrDefaultValueProperties
{
@@ -147,7 +195,7 @@ namespace Newtonsoft.Json.Serialization
ContractType = JsonContractType.Object;
Properties = new JsonPropertyCollection(UnderlyingType);
ConstructorParameters = new JsonPropertyCollection(UnderlyingType);
CreatorParameters = new JsonPropertyCollection(UnderlyingType);
}
#if !(NETFX_CORE || PORTABLE40 || PORTABLE)
@@ -407,17 +407,17 @@ namespace Newtonsoft.Json.Serialization
{
case JsonContractType.Object:
{
bool createdFromNonDefaultConstructor = false;
bool createdFromNonDefaultCreator = false;
JsonObjectContract objectContract = (JsonObjectContract)contract;
object targetObject;
// check that if type name handling is being used that the existing value is compatible with the specified type
if (existingValue != null && (resolvedObjectType == objectType || resolvedObjectType.IsAssignableFrom(existingValue.GetType())))
targetObject = existingValue;
else
targetObject = CreateNewObject(reader, objectContract, member, containerMember, id, out createdFromNonDefaultConstructor);
targetObject = CreateNewObject(reader, objectContract, member, containerMember, id, out createdFromNonDefaultCreator);
// don't populate if read from non-default constructor because the object has already been read
if (createdFromNonDefaultConstructor)
// don't populate if read from non-default creator because the object has already been read
if (createdFromNonDefaultCreator)
return targetObject;
return PopulateObject(targetObject, reader, objectContract, member, id);
@@ -449,10 +449,10 @@ namespace Newtonsoft.Json.Serialization
if (existingValue == null)
{
bool createdFromNonDefaultConstructor;
IDictionary dictionary = CreateNewDictionary(reader, dictionaryContract, out createdFromNonDefaultConstructor);
bool createdFromNonDefaultCreator;
IDictionary dictionary = CreateNewDictionary(reader, dictionaryContract, out createdFromNonDefaultCreator);
if (createdFromNonDefaultConstructor)
if (createdFromNonDefaultCreator)
{
if (id != null)
throw JsonSerializationException.Create(reader, "Cannot preserve reference to readonly dictionary, or dictionary created from a non-default constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
@@ -469,7 +469,7 @@ namespace Newtonsoft.Json.Serialization
PopulateDictionary(dictionary, reader, dictionaryContract, member, id);
if (createdFromNonDefaultConstructor)
if (createdFromNonDefaultCreator)
{
return dictionaryContract.ParametrizedCreator(null , new object[] { dictionary });
}
@@ -745,10 +745,10 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
if (existingValue == null)
{
bool createdFromNonDefaultConstructor;
IList list = CreateNewList(reader, arrayContract, out createdFromNonDefaultConstructor);
bool createdFromNonDefaultCreator;
IList list = CreateNewList(reader, arrayContract, out createdFromNonDefaultCreator);
if (createdFromNonDefaultConstructor)
if (createdFromNonDefaultCreator)
{
if (id != null)
throw JsonSerializationException.Create(reader, "Cannot preserve reference to array or readonly list, or list created from a non-default constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
@@ -768,7 +768,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
else
PopulateMultidimensionalArray(list, reader, arrayContract, member, id);
if (createdFromNonDefaultConstructor)
if (createdFromNonDefaultCreator)
{
if (arrayContract.IsMultidimensionalArray)
{
@@ -1003,7 +1003,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
return true;
}
private IList CreateNewList(JsonReader reader, JsonArrayContract contract, out bool createdFromNonDefaultConstructor)
private IList CreateNewList(JsonReader reader, JsonArrayContract contract, out bool createdFromNonDefaultCreator)
{
// some types like non-generic IEnumerable can be serialized but not deserialized
if (!contract.CanDeserialize)
@@ -1011,7 +1011,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
if (contract.IsReadOnlyOrFixedSize)
{
createdFromNonDefaultConstructor = true;
createdFromNonDefaultCreator = true;
IList list = contract.CreateTemporaryCollection();
if (contract.ShouldCreateWrapper)
@@ -1026,12 +1026,12 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
if (contract.ShouldCreateWrapper)
list = contract.CreateWrapper(list);
createdFromNonDefaultConstructor = false;
createdFromNonDefaultCreator = false;
return (IList)list;
}
else if (contract.ParametrizedCreator != null)
{
createdFromNonDefaultConstructor = true;
createdFromNonDefaultCreator = true;
return contract.CreateTemporaryCollection();
}
else
@@ -1040,11 +1040,11 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
}
}
private IDictionary CreateNewDictionary(JsonReader reader, JsonDictionaryContract contract, out bool createdFromNonDefaultConstructor)
private IDictionary CreateNewDictionary(JsonReader reader, JsonDictionaryContract contract, out bool createdFromNonDefaultCreator)
{
if (contract.IsReadOnlyOrFixedSize)
{
createdFromNonDefaultConstructor = true;
createdFromNonDefaultCreator = true;
return contract.CreateTemporaryDictionary();
}
else if (contract.DefaultCreator != null && (!contract.DefaultCreatorNonPublic || Serializer._constructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor))
@@ -1054,12 +1054,12 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
if (contract.ShouldCreateWrapper)
dictionary = contract.CreateWrapper(dictionary);
createdFromNonDefaultConstructor = false;
createdFromNonDefaultCreator = false;
return (IDictionary)dictionary;
}
else if (contract.ParametrizedCreator != null)
{
createdFromNonDefaultConstructor = true;
createdFromNonDefaultCreator = true;
return contract.CreateTemporaryDictionary();
}
else
@@ -1568,9 +1568,9 @@ To fix this error either change the environment to be fully trusted, change the
}
#endif
private object CreateObjectFromNonDefaultConstructor(JsonReader reader, JsonObjectContract contract, JsonProperty containerProperty, MethodCall<object, object> constructorInfo, string id)
private object CreateObjectUsingCreatorWithParameters(JsonReader reader, JsonObjectContract contract, JsonProperty containerProperty, Func<object[], object> creator, string id)
{
ValidationUtils.ArgumentNotNull(constructorInfo, "constructorInfo");
ValidationUtils.ArgumentNotNull(creator, "creator");
// only need to keep a track of properies presence if they are required or a value should be defaulted if missing
Dictionary<JsonProperty, PropertyPresence> propertiesPresence = (contract.HasRequiredOrDefaultValueProperties || HasFlag(Serializer._defaultValueHandling, DefaultValueHandling.Populate))
@@ -1581,34 +1581,34 @@ To fix this error either change the environment to be fully trusted, change the
if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info)
{
string underlyingMethodName = constructorInfo.GetMethodInfo().Name;
TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Deserializing {0} using custom method '{1}'.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType, underlyingMethodName)), null);
string parameters = string.Join(", ", contract.CreatorParameters.Select(p => p.PropertyName).ToArray());
TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Deserializing {0} using creator with parameters: {1}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType, parameters)), null);
}
IDictionary<string, object> extensionData;
IDictionary<JsonProperty, object> propertyValues = ResolvePropertyAndConstructorValues(contract, containerProperty, reader, objectType, out extensionData);
IDictionary<JsonProperty, object> propertyValues = ResolvePropertyAndCreatorValues(contract, containerProperty, reader, objectType, out extensionData);
IDictionary<JsonProperty, object> constructorParameters = contract.ConstructorParameters.ToDictionary(p => p, p => (object)null);
IDictionary<JsonProperty, object> creatorParameters = contract.CreatorParameters.ToDictionary(p => p, p => (object)null);
IDictionary<JsonProperty, object> remainingPropertyValues = new Dictionary<JsonProperty, object>();
foreach (KeyValuePair<JsonProperty, object> propertyValue in propertyValues)
{
JsonProperty matchingConstructorParameter = constructorParameters.ForgivingCaseSensitiveFind(kv => kv.Key.PropertyName, propertyValue.Key.PropertyName).Key;
if (matchingConstructorParameter != null)
constructorParameters[matchingConstructorParameter] = propertyValue.Value;
JsonProperty matchingCreatorParameter = creatorParameters.ForgivingCaseSensitiveFind(kv => kv.Key.PropertyName, propertyValue.Key.PropertyName).Key;
if (matchingCreatorParameter != null)
creatorParameters[matchingCreatorParameter] = propertyValue.Value;
else
remainingPropertyValues.Add(propertyValue);
if (propertiesPresence != null)
{
// map from constructor property to normal property
// map from creator property to normal property
var property = propertiesPresence.Keys.FirstOrDefault(p => p.PropertyName == propertyValue.Key.PropertyName);
if (property != null)
propertiesPresence[property] = (propertyValue.Value == null) ? PropertyPresence.Null : PropertyPresence.Value;
}
}
object createdObject = constructorInfo(null, constructorParameters.Values.ToArray());
object createdObject = creator(creatorParameters.Values.ToArray());
if (id != null)
AddReference(reader, id, createdObject);
@@ -1692,7 +1692,7 @@ To fix this error either change the environment to be fully trusted, change the
return value;
}
private IDictionary<JsonProperty, object> ResolvePropertyAndConstructorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType, out IDictionary<string, object> extensionData)
private IDictionary<JsonProperty, object> ResolvePropertyAndCreatorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType, out IDictionary<string, object> extensionData)
{
extensionData = (contract.ExtensionDataSetter != null) ? new Dictionary<string, object>() : null;
@@ -1707,7 +1707,7 @@ To fix this error either change the environment to be fully trusted, change the
// attempt exact case match first
// then try match ignoring case
JsonProperty property = contract.ConstructorParameters.GetClosestMatchProperty(memberName) ??
JsonProperty property = contract.CreatorParameters.GetClosestMatchProperty(memberName) ??
contract.Properties.GetClosestMatchProperty(memberName);
if (property != null)
@@ -1816,42 +1816,42 @@ To fix this error either change the environment to be fully trusted, change the
return (reader.TokenType != JsonToken.None);
}
public object CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, string id, out bool createdFromNonDefaultConstructor)
public object CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, string id, out bool createdFromNonDefaultCreator)
{
object newObject = null;
if (!objectContract.IsInstantiable)
throw JsonSerializationException.Create(reader, "Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, objectContract.UnderlyingType));
if (objectContract.OverrideConstructor != null)
if (objectContract.OverrideCreator != null)
{
if (objectContract.OverrideConstructor.GetParameters().Length > 0)
if (objectContract.CreatorParameters.Count > 0)
{
createdFromNonDefaultConstructor = true;
return CreateObjectFromNonDefaultConstructor(reader, objectContract, containerMember, objectContract.ParametrizedCreator, id);
createdFromNonDefaultCreator = true;
return CreateObjectUsingCreatorWithParameters(reader, objectContract, containerMember, objectContract.OverrideCreator, id);
}
newObject = objectContract.OverrideConstructor.Invoke(null);
newObject = objectContract.OverrideCreator(new object[0]);
}
else if (objectContract.DefaultCreator != null &&
(!objectContract.DefaultCreatorNonPublic || Serializer._constructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor || objectContract.ParametrizedConstructor == null))
(!objectContract.DefaultCreatorNonPublic || Serializer._constructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor || objectContract.ParametrizedCreator == null))
{
// use the default constructor if it is...
// public
// non-public and the user has change constructor handling settings
// non-public and there is no other constructor
// non-public and there is no other creator
newObject = objectContract.DefaultCreator();
}
else if (objectContract.ParametrizedCreator != null)
{
createdFromNonDefaultConstructor = true;
return CreateObjectFromNonDefaultConstructor(reader, objectContract, containerMember, objectContract.ParametrizedCreator, id);
createdFromNonDefaultCreator = true;
return CreateObjectUsingCreatorWithParameters(reader, objectContract, containerMember, objectContract.ParametrizedCreator, id);
}
if (newObject == null)
throw JsonSerializationException.Create(reader, "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, objectContract.UnderlyingType));
createdFromNonDefaultConstructor = false;
createdFromNonDefaultCreator = false;
return newObject;
}
@@ -54,13 +54,26 @@ namespace Newtonsoft.Json.Utilities
for (int i = 0; i < parametersInfo.Length; i++)
{
Type parameterType = parametersInfo[i].ParameterType;
Expression indexExpression = Expression.Constant(i);
Expression paramAccessorExpression = Expression.ArrayIndex(argsParameterExpression, indexExpression);
paramAccessorExpression = EnsureCastExpression(paramAccessorExpression, parametersInfo[i].ParameterType);
Expression argExpression;
argsExpression[i] = paramAccessorExpression;
if (parameterType.IsValueType())
{
BinaryExpression ensureValueTypeNotNull = Expression.Coalesce(paramAccessorExpression, Expression.New(parameterType));
argExpression = EnsureCastExpression(ensureValueTypeNotNull, parameterType);
}
else
{
argExpression = EnsureCastExpression(paramAccessorExpression, parameterType);
}
argsExpression[i] = argExpression;
}
Expression callExpression;