-Changed serializer to fallback to use a private default constructor
-Changed error message when deserializing a JSON object/array onto the wrong kind of type to be more descriptive
This commit is contained in:
@@ -405,10 +405,11 @@ DefaultValueHandling controls how Json.NET uses default values set using the .NE
|
|||||||
<div class="summary">
|
<div class="summary">
|
||||||
By default Json.NET will first look for a constructor
|
By default Json.NET will first look for a constructor
|
||||||
marked with the JsonConstructorAttribute, then look for a public default
|
marked with the JsonConstructorAttribute, then look for a public default
|
||||||
constructor (a constructor that doesn't take any arguments) and finally check if
|
constructor (a constructor that doesn't take any arguments), then check if
|
||||||
the class has a single public constructor with arguments. If the class has
|
the class has a single public constructor with arguments and finally check
|
||||||
multiple public constructors with arguments an error will be thrown. This can be
|
for a non-public default constructor. If the class has multiple public
|
||||||
fixed by marking one of the constructors with the JsonConstructorAttribute.
|
constructors with arguments an error will be thrown. This can be fixed by
|
||||||
|
marking one of the constructors with the JsonConstructorAttribute.
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
</td>
|
</td>
|
||||||
@@ -419,8 +420,8 @@ DefaultValueHandling controls how Json.NET uses default values set using the .NE
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="summary">
|
<div class="summary">
|
||||||
Json.NET will fallback to
|
Json.NET will use a classes private default constructor before constructors
|
||||||
using a classes private default constructor if available.
|
with arguments if available.
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -703,21 +703,6 @@ namespace Newtonsoft.Json.Tests
|
|||||||
}, "JObject.ToString");
|
}, "JObject.ToString");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void RecursiveLoop()
|
|
||||||
{
|
|
||||||
JArray a1 = new JArray();
|
|
||||||
JArray a2 = new JArray();
|
|
||||||
JArray a3 = new JArray();
|
|
||||||
JArray a4 = new JArray();
|
|
||||||
|
|
||||||
a1.Add(a2);
|
|
||||||
a2.Add(a3);
|
|
||||||
a3.Add(a4);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void NestedJToken()
|
public void NestedJToken()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -72,5 +72,5 @@ using System.Security;
|
|||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
[assembly: AssemblyVersion("4.0.8.0")]
|
[assembly: AssemblyVersion("4.0.8.0")]
|
||||||
#if !PocketPC
|
#if !PocketPC
|
||||||
[assembly: AssemblyFileVersion("4.0.8.14707")]
|
[assembly: AssemblyFileVersion("4.0.8.14710")]
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ namespace Newtonsoft.Json.Tests.Serialization
|
|||||||
public class ConstructorHandlingTests : TestFixtureBase
|
public class ConstructorHandlingTests : TestFixtureBase
|
||||||
{
|
{
|
||||||
[Test]
|
[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, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Line 1, position 6.")]
|
public void UsePrivateConstructorIfThereAreMultipleConstructorsWithParametersAndNothingToFallbackTo()
|
||||||
public void FailWithPrivateConstructorAndDefault()
|
|
||||||
{
|
{
|
||||||
string json = @"{Name:""Name!""}";
|
string json = @"{Name:""Name!""}";
|
||||||
|
|
||||||
JsonConvert.DeserializeObject<PrivateConstructorTestClass>(json);
|
var c = JsonConvert.DeserializeObject<PrivateConstructorTestClass>(json);
|
||||||
|
|
||||||
|
Assert.AreEqual("Name!", c.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
@@ -1905,7 +1905,9 @@ keyword such as type of business.""
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[ExpectedException(typeof (JsonSerializationException), ExpectedMessage = @"Cannot deserialize JSON array into type 'Newtonsoft.Json.Tests.TestObjects.Person'. Line 1, position 1.")]
|
[ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Cannot deserialize JSON array (i.e. [1,2,3]) into type 'Newtonsoft.Json.Tests.TestObjects.Person'.
|
||||||
|
The deserialized type must be an array or implement a collection interface like IEnumerable, ICollection or IList.
|
||||||
|
To force JSON arrays to deserialize add the JsonArrayAttribute to the type. Line 1, position 1.")]
|
||||||
public void CannotDeserializeArrayIntoObject()
|
public void CannotDeserializeArrayIntoObject()
|
||||||
{
|
{
|
||||||
string json = @"[]";
|
string json = @"[]";
|
||||||
@@ -1914,7 +1916,9 @@ keyword such as type of business.""
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[ExpectedException(typeof (JsonSerializationException), ExpectedMessage = @"Cannot deserialize JSON object into type 'System.Collections.Generic.List`1[Newtonsoft.Json.Tests.TestObjects.Person]'. Line 1, position 2.")]
|
[ExpectedException(typeof (JsonSerializationException), ExpectedMessage = @"Cannot deserialize JSON object (i.e. {""name"":""value""}) into type 'System.Collections.Generic.List`1[Newtonsoft.Json.Tests.TestObjects.Person]'.
|
||||||
|
The deserialized type should be a normal .NET type (i.e. not a primitive type like integer, not a collection type like an array or List<T>) or a dictionary type (i.e. Dictionary<TKey, TValue>).
|
||||||
|
To force JSON objects to deserialize add the JsonObjectAttribute to the type. Line 1, position 2.")]
|
||||||
public void CannotDeserializeObjectIntoArray()
|
public void CannotDeserializeObjectIntoArray()
|
||||||
{
|
{
|
||||||
string json = @"{}";
|
string json = @"{}";
|
||||||
@@ -5550,9 +5554,37 @@ Parameter name: value")]
|
|||||||
Assert.AreEqual("Pre", c2.PreField);
|
Assert.AreEqual("Pre", c2.PreField);
|
||||||
Assert.AreEqual("Post", c2.PostField);
|
Assert.AreEqual("Post", c2.PostField);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void PrivateConstructor()
|
||||||
|
{
|
||||||
|
var person = PersonWithPrivateConstructor.CreatePerson();
|
||||||
|
person.Name = "John Doe";
|
||||||
|
person.Age = 25;
|
||||||
|
|
||||||
|
var serializedPerson = JsonConvert.SerializeObject(person);
|
||||||
|
var roundtrippedPerson = JsonConvert.DeserializeObject<PersonWithPrivateConstructor>(serializedPerson);
|
||||||
|
|
||||||
|
Assert.AreEqual(person.Name, roundtrippedPerson.Name);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class PersonWithPrivateConstructor
|
||||||
|
{
|
||||||
|
private PersonWithPrivateConstructor()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public static PersonWithPrivateConstructor CreatePerson()
|
||||||
|
{
|
||||||
|
return new PersonWithPrivateConstructor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public int Age { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class DateTimeWrapper
|
public class DateTimeWrapper
|
||||||
{
|
{
|
||||||
public DateTime Value { get; set; }
|
public DateTime Value { get; set; }
|
||||||
|
|||||||
@@ -33,5 +33,15 @@ namespace Newtonsoft.Json.Tests.TestObjects
|
|||||||
private PrivateConstructorTestClass()
|
private PrivateConstructorTestClass()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// multiple constructors with arguments so the serializer doesn't know what to fall back to
|
||||||
|
private PrivateConstructorTestClass(object a)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiple constructors with arguments so the serializer doesn't know what to fall back to
|
||||||
|
private PrivateConstructorTestClass(object a, object b)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,11 +36,11 @@ namespace Newtonsoft.Json
|
|||||||
public enum ConstructorHandling
|
public enum ConstructorHandling
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// First attempt to use the public default constructor then fall back to single paramatized constructor.
|
/// First attempt to use the public default constructor, then fall back to single paramatized constructor, then the non-public default constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Default = 0,
|
Default = 0,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allow Json.NET to use a non-public default constructor.
|
/// Json.NET will use a non-public default constructor before falling back to a paramatized constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AllowNonPublicDefaultConstructor = 1
|
AllowNonPublicDefaultConstructor = 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ using System.Security;
|
|||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
[assembly: AssemblyVersion("4.0.8.0")]
|
[assembly: AssemblyVersion("4.0.8.0")]
|
||||||
#if !PocketPC
|
#if !PocketPC
|
||||||
[assembly: AssemblyFileVersion("4.0.8.14707")]
|
[assembly: AssemblyFileVersion("4.0.8.14710")]
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[assembly: CLSCompliant(true)]
|
[assembly: CLSCompliant(true)]
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ namespace Newtonsoft.Json.Serialization
|
|||||||
public Func<object> DefaultCreator { get; set; }
|
public Func<object> DefaultCreator { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether [default creator non public].
|
/// Gets or sets a value indicating whether the default creator is non public.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value><c>true</c> if the default object creator is non-public; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if the default object creator is non-public; otherwise, <c>false</c>.</value>
|
||||||
public bool DefaultCreatorNonPublic { get; set; }
|
public bool DefaultCreatorNonPublic { get; set; }
|
||||||
|
|||||||
@@ -468,7 +468,9 @@ namespace Newtonsoft.Json.Serialization
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
throw CreateSerializationException(reader, "Cannot deserialize JSON object into type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType));
|
throw CreateSerializationException(reader, @"Cannot deserialize JSON object (i.e. {{""name"":""value""}}) into type '{0}'.
|
||||||
|
The deserialized type should be a normal .NET type (i.e. not a primitive type like integer, not a collection type like an array or List<T>) or a dictionary type (i.e. Dictionary<TKey, TValue>).
|
||||||
|
To force JSON objects to deserialize add the JsonObjectAttribute to the type.".FormatWith(CultureInfo.InvariantCulture, objectType));
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsonArrayContract EnsureArrayContract(JsonReader reader, Type objectType, JsonContract contract)
|
private JsonArrayContract EnsureArrayContract(JsonReader reader, Type objectType, JsonContract contract)
|
||||||
@@ -478,7 +480,9 @@ namespace Newtonsoft.Json.Serialization
|
|||||||
|
|
||||||
JsonArrayContract arrayContract = contract as JsonArrayContract;
|
JsonArrayContract arrayContract = contract as JsonArrayContract;
|
||||||
if (arrayContract == null)
|
if (arrayContract == null)
|
||||||
throw CreateSerializationException(reader, "Cannot deserialize JSON array into type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType));
|
throw CreateSerializationException(reader, @"Cannot deserialize JSON array (i.e. [1,2,3]) into type '{0}'.
|
||||||
|
The deserialized type must be an array or implement a collection interface like IEnumerable, ICollection or IList.
|
||||||
|
To force JSON arrays to deserialize add the JsonArrayAttribute to the type.".FormatWith(CultureInfo.InvariantCulture, objectType));
|
||||||
|
|
||||||
return arrayContract;
|
return arrayContract;
|
||||||
}
|
}
|
||||||
@@ -954,8 +958,12 @@ namespace Newtonsoft.Json.Serialization
|
|||||||
newObject = contract.OverrideConstructor.Invoke(null);
|
newObject = contract.OverrideConstructor.Invoke(null);
|
||||||
}
|
}
|
||||||
else if (contract.DefaultCreator != null &&
|
else if (contract.DefaultCreator != null &&
|
||||||
(!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor))
|
(!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor || contract.ParametrizedConstructor == 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
|
||||||
newObject = contract.DefaultCreator();
|
newObject = contract.DefaultCreator();
|
||||||
}
|
}
|
||||||
else if (contract.ParametrizedConstructor != null)
|
else if (contract.ParametrizedConstructor != null)
|
||||||
|
|||||||
Reference in New Issue
Block a user