-Performance

-Added ConstructorHandling to allow object creation using non-public default constructors
This commit is contained in:
JamesNK
2009-10-10 06:09:15 +00:00
parent 75627d6292
commit 7e75bbdcf7
26 changed files with 388 additions and 54 deletions
@@ -72,6 +72,7 @@
<Compile Include="Schema\JsonSchemaModelBuilderTests.cs" />
<Compile Include="Schema\JsonSchemaNodeTests.cs" />
<Compile Include="Serialization\CamelCasePropertyNamesContractResolverTests.cs" />
<Compile Include="Serialization\ConstructorHandlingTests.cs" />
<Compile Include="Serialization\EntitiesSerializationTests.cs" />
<Compile Include="Serialization\JsonSerializerTest.cs" />
<Compile Include="Serialization\MissingMemberHandlingTests.cs" />
@@ -121,6 +122,8 @@
<Compile Include="TestObjects\ObjectArrayPropertyTest.cs" />
<Compile Include="TestObjects\PersonRaw.cs" />
<Compile Include="TestObjects\PhoneNumber.cs" />
<Compile Include="TestObjects\PrivateConstructorTestClass.cs" />
<Compile Include="TestObjects\PrivateConstructorWithPublicParametizedConstructorTestClass.cs" />
<Compile Include="TestObjects\PrivateMembersClass.cs" />
<Compile Include="TestObjects\PropertyCase.cs" />
<Compile Include="TestObjects\RequestOnly.cs" />
@@ -72,6 +72,7 @@
<Compile Include="Linq\ComponentModel\JPropertyDescriptorTests.cs" />
<Compile Include="Linq\ComponentModel\JTypeDescriptionProviderTests.cs" />
<Compile Include="Linq\ComponentModel\JTypeDescriptorTests.cs" />
<Compile Include="Serialization\ConstructorHandlingTests.cs" />
<Compile Include="TestObjects\Content.cs" />
<Compile Include="TestObjects\DictionaryInterfaceClass.cs" />
<Compile Include="Serialization\EntitiesSerializationTests.cs" />
@@ -86,6 +87,8 @@
<Compile Include="TestObjects\NonRequest.cs" />
<Compile Include="TestObjects\ObjectArrayPropertyTest.cs" />
<Compile Include="Serialization\PopulateTests.cs" />
<Compile Include="TestObjects\PrivateConstructorTestClass.cs" />
<Compile Include="TestObjects\PrivateConstructorWithPublicParametizedConstructorTestClass.cs" />
<Compile Include="TestObjects\PropertyCase.cs" />
<Compile Include="TestObjects\RequestOnly.cs" />
<Compile Include="TestObjects\RoleTransfer.cs" />
@@ -78,6 +78,7 @@
<Compile Include="Schema\JsonSchemaModelBuilderTests.cs" />
<Compile Include="Schema\JsonSchemaNodeTests.cs" />
<Compile Include="Serialization\CamelCasePropertyNamesContractResolverTests.cs" />
<Compile Include="Serialization\ConstructorHandlingTests.cs" />
<Compile Include="Serialization\EntitiesSerializationTests.cs" />
<Compile Include="Serialization\JsonSerializerTest.cs" />
<Compile Include="Serialization\MissingMemberHandlingTests.cs" />
@@ -127,6 +128,8 @@
<Compile Include="TestObjects\ObjectArrayPropertyTest.cs" />
<Compile Include="TestObjects\PersonRaw.cs" />
<Compile Include="TestObjects\PhoneNumber.cs" />
<Compile Include="TestObjects\PrivateConstructorTestClass.cs" />
<Compile Include="TestObjects\PrivateConstructorWithPublicParametizedConstructorTestClass.cs" />
<Compile Include="TestObjects\PrivateMembersClass.cs" />
<Compile Include="TestObjects\PropertyCase.cs" />
<Compile Include="TestObjects\RequestOnly.cs" />
@@ -109,6 +109,9 @@
<Compile Include="Linq\ComponentModel\JPropertyDescriptorTests.cs" />
<Compile Include="Linq\ComponentModel\JTypeDescriptionProviderTests.cs" />
<Compile Include="Linq\ComponentModel\JTypeDescriptorTests.cs" />
<Compile Include="Serialization\ConstructorHandlingTests.cs" />
<Compile Include="TestObjects\PrivateConstructorTestClass.cs" />
<Compile Include="TestObjects\PrivateConstructorWithPublicParametizedConstructorTestClass.cs" />
<Compile Include="TestObjects\Content.cs" />
<Compile Include="TestObjects\DateTimeErrorObjectCollection.cs" />
<Compile Include="Serialization\EntitiesSerializationTests.cs" />
@@ -167,7 +167,7 @@ namespace Newtonsoft.Json.Tests
timed.Stop();
Console.WriteLine("Serialize method: {0}", method);
Console.WriteLine("Deserialize method: {0}", method);
Console.WriteLine("{0} ms", timed.ElapsedMilliseconds);
Console.WriteLine(value);
Console.WriteLine();
@@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using NUnit.Framework;
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.Serialization.PrivateConstructorTestClass. A class should either have a default constructor or only one constructor with arguments.")]
public void FailWithPrivateConstructorAndDefault()
{
string json = @"{Name:""Name!""}";
JsonConvert.DeserializeObject<PrivateConstructorTestClass>(json);
}
[Test]
public void SuccessWithPrivateConstructorAndAllowNonPublic()
{
string json = @"{Name:""Name!""}";
PrivateConstructorTestClass c = JsonConvert.DeserializeObject<PrivateConstructorTestClass>(json,
new JsonSerializerSettings
{
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
});
Assert.IsNotNull(c);
Assert.AreEqual("Name!", c.Name);
}
[Test]
[ExpectedException(typeof(TargetInvocationException))]
public void FailWithPrivateConstructorPlusParametizedAndDefault()
{
string json = @"{Name:""Name!""}";
PrivateConstructorWithPublicParametizedConstructorTestClass c = JsonConvert.DeserializeObject<PrivateConstructorWithPublicParametizedConstructorTestClass>(json);
}
[Test]
public void SuccessWithPrivateConstructorPlusParametizedAndAllowNonPublic()
{
string json = @"{Name:""Name!""}";
PrivateConstructorWithPublicParametizedConstructorTestClass c = JsonConvert.DeserializeObject<PrivateConstructorWithPublicParametizedConstructorTestClass>(json,
new JsonSerializerSettings
{
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
});
Assert.IsNotNull(c);
Assert.AreEqual("Name!", c.Name);
Assert.AreEqual(1, c.Age);
}
}
}
@@ -1756,7 +1756,7 @@ keyword such as type of business.""
}
[Test]
[ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Unable to determine which constructor to use for type Newtonsoft.Json.Tests.TestObjects.Event. A class with no default constructor should have 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 or only one constructor with arguments.")]
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)\/""}";
@@ -0,0 +1,37 @@
#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
namespace Newtonsoft.Json.Tests.TestObjects
{
public class PrivateConstructorTestClass
{
public string Name { get; set; }
public int Age { get; set; }
private PrivateConstructorTestClass()
{
}
}
}
@@ -0,0 +1,45 @@
#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
using System;
namespace Newtonsoft.Json.Tests.TestObjects
{
public class PrivateConstructorWithPublicParametizedConstructorTestClass
{
public string Name { get; set; }
public int Age { get; set; }
private PrivateConstructorWithPublicParametizedConstructorTestClass()
{
Age = 1;
}
public PrivateConstructorWithPublicParametizedConstructorTestClass(string dummy)
{
throw new Exception("Should never get here.");
}
}
}
@@ -0,0 +1,47 @@
#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Newtonsoft.Json
{
/// <summary>
/// Specifies how constructors are used when initializing objects during deserialization by the <see cref="JsonSerializer"/>.
/// </summary>
public enum ConstructorHandling
{
/// <summary>
/// First attempt to use the public default constructor then fall back to single paramatized constructor.
/// </summary>
Default = 0,
/// <summary>
/// Allow Json.NET to use a non-public default constructor.
/// </summary>
AllowNonPublicDefaultConstructor = 1
}
}
+19
View File
@@ -47,6 +47,7 @@ namespace Newtonsoft.Json
private ObjectCreationHandling _objectCreationHandling;
private NullValueHandling _nullValueHandling;
private DefaultValueHandling _defaultValueHandling;
private ConstructorHandling _constructorHandling;
private JsonConverterCollection _converters;
private IContractResolver _contractResolver;
private IReferenceResolver _referenceResolver;
@@ -204,6 +205,22 @@ namespace Newtonsoft.Json
}
}
/// <summary>
/// Gets or sets how constructors are used during deserialization.
/// </summary>
/// <value>The constructor handling.</value>
public virtual ConstructorHandling ConstructorHandling
{
get { return _constructorHandling; }
set
{
if (value < ConstructorHandling.Default || value > ConstructorHandling.AllowNonPublicDefaultConstructor)
throw new ArgumentOutOfRangeException("value");
_constructorHandling = value;
}
}
/// <summary>
/// Gets a collection <see cref="JsonConverter"/> that will be used during serialization.
/// </summary>
@@ -247,6 +264,7 @@ namespace Newtonsoft.Json
_defaultValueHandling = JsonSerializerSettings.DefaultDefaultValueHandling;
_objectCreationHandling = JsonSerializerSettings.DefaultObjectCreationHandling;
_preserveReferencesHandling = JsonSerializerSettings.DefaultPreserveReferencesHandling;
_constructorHandling = JsonSerializerSettings.DefaultConstructorHandling;
_typeNameHandling = JsonSerializerSettings.DefaultTypeNameHandling;
_binder = DefaultSerializationBinder.Instance;
}
@@ -272,6 +290,7 @@ namespace Newtonsoft.Json
jsonSerializer.ObjectCreationHandling = settings.ObjectCreationHandling;
jsonSerializer.NullValueHandling = settings.NullValueHandling;
jsonSerializer.DefaultValueHandling = settings.DefaultValueHandling;
jsonSerializer.ConstructorHandling = settings.ConstructorHandling;
if (settings.ContractResolver != null)
jsonSerializer.ContractResolver = settings.ContractResolver;
@@ -19,6 +19,7 @@ namespace Newtonsoft.Json
internal const DefaultValueHandling DefaultDefaultValueHandling = DefaultValueHandling.Include;
internal const ObjectCreationHandling DefaultObjectCreationHandling = ObjectCreationHandling.Auto;
internal const PreserveReferencesHandling DefaultPreserveReferencesHandling = PreserveReferencesHandling.None;
internal const ConstructorHandling DefaultConstructorHandling = ConstructorHandling.Default;
internal const TypeNameHandling DefaultTypeNameHandling = TypeNameHandling.None;
/// <summary>
@@ -61,6 +62,11 @@ namespace Newtonsoft.Json
/// </summary>
/// <value>The type name handling.</value>
public TypeNameHandling TypeNameHandling { get; set; }
/// <summary>
/// Gets or sets how constructors are used during deserialization.
/// </summary>
/// <value>The constructor handling.</value>
public ConstructorHandling ConstructorHandling { get; set; }
/// <summary>
/// Gets or sets the contract resolver used by the serializer when
+13 -1
View File
@@ -347,6 +347,10 @@ namespace Newtonsoft.Json
SetStateBasedOnCurrent();
ClearCurrentChar();
return false;
case ' ':
// eat whitespace
ClearCurrentChar();
break;
default:
if (char.IsWhiteSpace(_currentChar))
{
@@ -379,6 +383,9 @@ namespace Newtonsoft.Json
case ',':
SetToken(JsonToken.Undefined);
return true;
case ' ':
// eat
break;
default:
if (char.IsWhiteSpace(_currentChar))
{
@@ -547,6 +554,9 @@ namespace Newtonsoft.Json
case ')':
SetToken(JsonToken.EndConstructor);
return true;
case ' ':
// eat
break;
default:
if (char.IsWhiteSpace(_currentChar))
{
@@ -571,7 +581,7 @@ namespace Newtonsoft.Json
private bool EatWhitespace(bool oneOrMore)
{
bool whitespace = false;
while (char.IsWhiteSpace(_currentChar))
while (_currentChar == ' ' || char.IsWhiteSpace(_currentChar))
{
whitespace = true;
MoveNext();
@@ -736,6 +746,8 @@ namespace Newtonsoft.Json
if (CurrentState == State.Constructor || CurrentState == State.ConstructorStart)
return true;
break;
case ' ':
return true;
default:
if (char.IsWhiteSpace(_currentChar))
return true;
@@ -57,6 +57,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ConstructorHandling.cs" />
<Compile Include="Converters\BinaryConverter.cs" />
<Compile Include="Converters\CustomCreationConverter.cs" />
<Compile Include="Converters\EntityKeyMemberConverter.cs" />
@@ -77,6 +77,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ConstructorHandling.cs" />
<Compile Include="Converters\BinaryConverter.cs" />
<Compile Include="Converters\CustomCreationConverter.cs" />
<Compile Include="Converters\EntityKeyMemberConverter.cs" />
@@ -49,6 +49,7 @@
<Reference Include="System.Windows.Browser" />
</ItemGroup>
<ItemGroup>
<Compile Include="ConstructorHandling.cs" />
<Compile Include="Converters\BinaryConverter.cs" />
<Compile Include="Converters\CustomCreationConverter.cs" />
<Compile Include="Converters\EntityKeyMemberConverter.cs" />
@@ -90,6 +90,7 @@
<Compile Include="Converters\CustomCreationConverter.cs" />
<Compile Include="Converters\EntityKeyMemberConverter.cs" />
<Compile Include="Converters\StringEnumConverter.cs" />
<Compile Include="ConstructorHandling.cs" />
<Compile Include="Linq\ComponentModel\JPropertyDescriptor.cs" />
<Compile Include="Linq\ComponentModel\JTypeDescriptionProvider.cs" />
<Compile Include="Linq\ComponentModel\JTypeDescriptor.cs" />
@@ -140,10 +140,22 @@ namespace Newtonsoft.Json.Serialization
contract.MemberSerialization = JsonTypeReflector.GetObjectMemberSerialization(objectType);
contract.Properties.AddRange(CreateProperties(contract));
if (contract.DefaultContstructor == null || contract.DefaultContstructor.IsPrivate)
contract.ParametrizedConstructor = GetParametrizedConstructor(objectType);
return contract;
}
private ConstructorInfo GetParametrizedConstructor(Type objectType)
{
ConstructorInfo[] constructors = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
if (constructors.Length == 1)
return constructors[0];
else
return null;
}
private void InitializeContract(JsonContract contract)
{
JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(contract.UnderlyingType);
@@ -161,6 +173,10 @@ namespace Newtonsoft.Json.Serialization
}
#endif
contract.DefaultContstructor =
ReflectionUtils.GetDefaultConstructor(contract.CreatedType, false) ??
ReflectionUtils.GetDefaultConstructor(contract.CreatedType, true);
foreach (MethodInfo method in contract.UnderlyingType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
Type prevAttributeType = null;
@@ -25,7 +25,9 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Newtonsoft.Json.Utilities;
using System.Collections;
namespace Newtonsoft.Json.Serialization
{
@@ -34,9 +36,13 @@ namespace Newtonsoft.Json.Serialization
/// </summary>
public class JsonArrayContract : JsonContract
{
internal Type CollectionTypeToCreate { get; private set; }
internal Type CollectionItemType { get; private set; }
private bool _isCollectionItemTypeNullableType;
private Type _genericCollectionDefinitionType;
private Type _genericWrapperType;
private ConstructorInfo _genericWrapperConstructor;
/// <summary>
/// Initializes a new instance of the <see cref="JsonArrayContract"/> class.
/// </summary>
@@ -44,17 +50,40 @@ namespace Newtonsoft.Json.Serialization
public JsonArrayContract(Type underlyingType)
: base(underlyingType)
{
CollectionItemType = ReflectionUtils.GetCollectionItemType(UnderlyingType);
if (IsTypeGenericCollectionInterface(UnderlyingType))
if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(ICollection<>), out _genericCollectionDefinitionType))
{
Type itemType = ReflectionUtils.GetCollectionItemType(UnderlyingType);
CollectionTypeToCreate = ReflectionUtils.MakeGenericType(typeof(List<>), itemType);
CollectionItemType = _genericCollectionDefinitionType.GetGenericArguments()[0];
}
else
{
CollectionTypeToCreate = UnderlyingType;
CollectionItemType = ReflectionUtils.GetCollectionItemType(UnderlyingType);
}
if (CollectionItemType != null)
_isCollectionItemTypeNullableType = ReflectionUtils.IsNullableType(CollectionItemType);
if (IsTypeGenericCollectionInterface(UnderlyingType))
{
CreatedType = ReflectionUtils.MakeGenericType(typeof(List<>), CollectionItemType);
}
else
{
CreatedType = UnderlyingType;
}
}
internal IWrappedCollection CreateWrapper(object list)
{
if (list is IList && (CollectionItemType == null || !_isCollectionItemTypeNullableType))
return new CollectionWrapper<object>((IList)list);
if (_genericWrapperType == null)
{
_genericWrapperType = ReflectionUtils.MakeGenericType(typeof(CollectionWrapper<>), CollectionItemType);
_genericWrapperConstructor = _genericWrapperType.GetConstructor(new[] { _genericCollectionDefinitionType });
}
return (IWrappedCollection)_genericWrapperConstructor.Invoke(new[] { list });
}
private bool IsTypeGenericCollectionInterface(Type type)
@@ -40,6 +40,13 @@ namespace Newtonsoft.Json.Serialization
/// </summary>
/// <value>The underlying type for the contract.</value>
public Type UnderlyingType { get; private set; }
/// <summary>
/// Gets or sets the type created during deserialization.
/// </summary>
/// <value>The type created during deserialization.</value>
public Type CreatedType { get; set; }
/// <summary>
/// Gets or sets whether this type contract is serialized as a reference.
/// </summary>
@@ -72,6 +79,12 @@ namespace Newtonsoft.Json.Serialization
public MethodInfo OnSerializing { get; set; }
#endif
/// <summary>
/// Gets or sets the default contstructor used to create the object.
/// </summary>
/// <value>The default contstructor.</value>
public ConstructorInfo DefaultContstructor { get; set; }
/// <summary>
/// Gets or sets the method called when an error is thrown during the serialization of the object.
/// </summary>
@@ -25,7 +25,9 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Newtonsoft.Json.Utilities;
using System.Collections;
namespace Newtonsoft.Json.Serialization
{
@@ -34,10 +36,13 @@ namespace Newtonsoft.Json.Serialization
/// </summary>
public class JsonDictionaryContract : JsonContract
{
internal Type DictionaryTypeToCreate { get; private set; }
internal Type DictionaryKeyType { get; private set; }
internal Type DictionaryValueType { get; private set; }
private Type _genericCollectionDefinitionType;
private Type _genericWrapperType;
private ConstructorInfo _genericWrapperConstructor;
/// <summary>
/// Initializes a new instance of the <see cref="JsonDictionaryContract"/> class.
/// </summary>
@@ -47,21 +52,43 @@ namespace Newtonsoft.Json.Serialization
{
Type keyType;
Type valueType;
ReflectionUtils.GetDictionaryKeyValueTypes(UnderlyingType, out keyType, out valueType);
if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(IDictionary<,>), out _genericCollectionDefinitionType))
{
keyType = _genericCollectionDefinitionType.GetGenericArguments()[0];
valueType = _genericCollectionDefinitionType.GetGenericArguments()[1];
}
else
{
ReflectionUtils.GetDictionaryKeyValueTypes(UnderlyingType, out keyType, out valueType);
}
DictionaryKeyType = keyType;
DictionaryValueType = valueType;
if (IsTypeGenericDictionaryInterface(UnderlyingType))
{
DictionaryTypeToCreate = ReflectionUtils.MakeGenericType(typeof(Dictionary<,>), keyType, valueType);
CreatedType = ReflectionUtils.MakeGenericType(typeof(Dictionary<,>), keyType, valueType);
}
else
{
DictionaryTypeToCreate = underlyingType;
CreatedType = underlyingType;
}
}
internal IWrappedDictionary CreateWrapper(object dictionary)
{
if (dictionary is IDictionary)
return new DictionaryWrapper<object, object>((IDictionary)dictionary);
if (_genericWrapperType == null)
{
_genericWrapperType = ReflectionUtils.MakeGenericType(typeof(DictionaryWrapper<,>), DictionaryKeyType, DictionaryValueType);
_genericWrapperConstructor = _genericWrapperType.GetConstructor(new[] { _genericCollectionDefinitionType });
}
return (IWrappedDictionary)_genericWrapperConstructor.Invoke(new[] { dictionary });
}
private bool IsTypeGenericDictionaryInterface(Type type)
{
if (!type.IsGenericType)
@@ -24,6 +24,7 @@
#endregion
using System;
using System.Reflection;
namespace Newtonsoft.Json.Serialization
{
@@ -44,6 +45,12 @@ namespace Newtonsoft.Json.Serialization
/// <value>The object's properties.</value>
public JsonPropertyCollection Properties { get; private set; }
/// <summary>
/// Gets or sets the parametrized constructor used to create the object.
/// </summary>
/// <value>The parametrized constructor.</value>
public ConstructorInfo ParametrizedConstructor { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="JsonObjectContract"/> class.
/// </summary>
@@ -52,6 +59,7 @@ namespace Newtonsoft.Json.Serialization
: base(underlyingType)
{
Properties = new JsonPropertyCollection();
CreatedType = underlyingType;
}
}
}
@@ -435,7 +435,7 @@ namespace Newtonsoft.Json.Serialization
private object CreateAndPopulateDictionary(JsonReader reader, JsonDictionaryContract contract, string id)
{
IWrappedDictionary dictionary = CollectionUtils.CreateDictionaryWrapper(Activator.CreateInstance(contract.DictionaryTypeToCreate));
IWrappedDictionary dictionary = contract.CreateWrapper(contract.DefaultContstructor.Invoke(null));
PopulateDictionary(dictionary, reader, contract, id);
@@ -488,7 +488,7 @@ namespace Newtonsoft.Json.Serialization
private object CreateAndPopulateList(JsonReader reader, string reference, JsonArrayContract contract)
{
return CollectionUtils.CreateAndPopulateList(contract.CollectionTypeToCreate, (l, isTemporaryListReference) =>
return CollectionUtils.CreateAndPopulateList(contract.CreatedType, (l, isTemporaryListReference) =>
{
if (reference != null && isTemporaryListReference)
throw new JsonSerializationException("Cannot preserve reference to array or readonly list: {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
@@ -500,7 +500,7 @@ namespace Newtonsoft.Json.Serialization
if (contract.OnError != null && isTemporaryListReference)
throw new JsonSerializationException("Cannot call OnError on an array or readonly list: {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
PopulateList(CollectionUtils.CreateCollectionWrapper(l), reader, reference, contract);
PopulateList(contract.CreateWrapper(l), reader, reference, contract);
});
}
@@ -556,30 +556,24 @@ 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 (ReflectionUtils.HasDefaultConstructor(contract.UnderlyingType))
if (contract.DefaultContstructor != null &&
(contract.DefaultContstructor.IsPublic || _serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor))
{
newObject = Activator.CreateInstance(contract.UnderlyingType);
newObject = contract.DefaultContstructor.Invoke(null);
PopulateObject(newObject, reader, contract, id);
return newObject;
}
return CreateObjectFromNonDefaultConstructor(contract, reader);
return CreateObjectFromNonDefaultConstructor(reader, contract, id);
}
private object CreateObjectFromNonDefaultConstructor(JsonObjectContract contract, JsonReader reader)
private object CreateObjectFromNonDefaultConstructor(JsonReader reader, JsonObjectContract contract, string id)
{
Type objectType = contract.UnderlyingType;
ConstructorInfo[] constructors = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
if (constructors.Length == 0)
throw new JsonSerializationException("Could not find a public constructor for type {0}.".FormatWith(CultureInfo.InvariantCulture, objectType));
if (constructors.Length > 1)
throw new JsonSerializationException("Unable to determine which constructor to use for type {0}. A class with no default constructor should have only one constructor with arguments.".FormatWith(CultureInfo.InvariantCulture, objectType));
// object should have a single constructor
ConstructorInfo c = constructors[0];
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));
// create a dictionary to put retrieved values into
IDictionary<JsonProperty, object> propertyValues = contract.Properties.Where(p => !p.Ignored).ToDictionary(kv => kv, kv => (object)null);
@@ -621,7 +615,7 @@ namespace Newtonsoft.Json.Serialization
}
} while (!exit && reader.Read());
IDictionary<ParameterInfo, object> constructorParameters = c.GetParameters().ToDictionary(p => p, p => (object)null);
IDictionary<ParameterInfo, object> constructorParameters = contract.ParametrizedConstructor.GetParameters().ToDictionary(p => p, p => (object)null);
IDictionary<JsonProperty, object> remainingPropertyValues = new Dictionary<JsonProperty, object>();
foreach (KeyValuePair<JsonProperty, object> propertyValue in propertyValues)
@@ -633,7 +627,11 @@ namespace Newtonsoft.Json.Serialization
remainingPropertyValues.Add(propertyValue);
}
object createdObject = ReflectionUtils.CreateInstance(objectType, constructorParameters.Values.ToArray());
object createdObject = contract.ParametrizedConstructor.Invoke(constructorParameters.Values.ToArray());
if (id != null)
_serializer.ReferenceResolver.AddReference(id, createdObject);
contract.InvokeOnDeserializing(createdObject);
// go through unused values and set the newly created object's properties
@@ -94,6 +94,12 @@ namespace Newtonsoft.Json.Serialization
set { _serializer.TypeNameHandling = value; }
}
public override ConstructorHandling ConstructorHandling
{
get { return _serializer.ConstructorHandling; }
set { _serializer.ConstructorHandling = value; }
}
public override SerializationBinder Binder
{
get { return _serializer.Binder; }
@@ -334,17 +334,6 @@ namespace Newtonsoft.Json.Utilities
return result;
}
public static T[] CreateArray<T>(IEnumerable<T> enumerable)
{
ValidationUtils.ArgumentNotNull(enumerable, "enumerable");
if (enumerable is T[])
return (T[])enumerable;
List<T> tempList = new List<T>(enumerable);
return tempList.ToArray();
}
public static IList CreateGenericList(Type listType)
{
ValidationUtils.ArgumentNotNull(listType, "listType");
@@ -55,13 +55,30 @@ namespace Newtonsoft.Json.Utilities
}
public static bool HasDefaultConstructor(Type t)
{
return HasDefaultConstructor(t, false);
}
public static bool HasDefaultConstructor(Type t, bool nonPublic)
{
ValidationUtils.ArgumentNotNull(t, "t");
if (t.IsValueType)
return true;
return (t.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[0], null) != null);
return (GetDefaultConstructor(t, nonPublic) != null);
}
public static ConstructorInfo GetDefaultConstructor(Type t)
{
return GetDefaultConstructor(t, false);
}
public static ConstructorInfo GetDefaultConstructor(Type t, bool nonPublic)
{
BindingFlags accessModifier = (!nonPublic) ? BindingFlags.Public : BindingFlags.NonPublic;
return t.GetConstructor(accessModifier | BindingFlags.Instance, null, new Type[0], null);
}
public static bool IsNullable(Type t)
@@ -575,20 +592,11 @@ namespace Newtonsoft.Json.Utilities
ValidationUtils.ArgumentNotNullOrEmpty(innerTypes, "innerTypes");
ValidationUtils.ArgumentNotNull(instanceCreator, "createInstance");
Type specificType = MakeGenericType(genericTypeDefinition, CollectionUtils.CreateArray(innerTypes));
Type specificType = MakeGenericType(genericTypeDefinition, innerTypes.ToArray());
return instanceCreator(specificType, args);
}
static object CreateInstance(this Assembly a, string typeName, params object[] pars)
{
var t = a.GetType(typeName);
var c = t.GetConstructor(pars.Select(p => p.GetType()).ToArray());
if (c == null)
return null;
return c.Invoke(pars);
}
public static bool IsCompatibleValue(object value, Type type)
{
if (value == null && IsNullable(type))