-Fixed deserializing to a type when setting JToken extension data
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Tests.TestObjects;
|
||||
@@ -564,10 +565,7 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
|
||||
JObject bizzaroC2 = (JObject)c2.ExtensionData["Self"];
|
||||
|
||||
Assert.AreEqual("Name!", (string)bizzaroC2["Name"]);
|
||||
Assert.AreEqual(1, (int)bizzaroC2["Test"]);
|
||||
|
||||
Assert.AreEqual(1, (int)c2.ExtensionData["Test"]);
|
||||
Assert.AreEqual("1", (string)bizzaroC2["$ref"]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -833,5 +831,67 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
() => { JsonConvert.SerializeObject(new DocNoGetter()); },
|
||||
"Invalid extension data attribute on 'Newtonsoft.Json.Tests.Serialization.ExtensionDataTests+DocNoGetter'. Member 'Content' must have a getter.");
|
||||
}
|
||||
|
||||
public class Item
|
||||
{
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, JToken> ExtensionData;
|
||||
|
||||
public IEnumerable<string> Foo
|
||||
{
|
||||
get { yield return "foo"; yield return "bar"; }
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Deserialize_WriteJsonDirectlyToJToken()
|
||||
{
|
||||
JsonSerializer jsonSerializer = new JsonSerializer
|
||||
{
|
||||
TypeNameHandling = TypeNameHandling.Auto
|
||||
};
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
jsonSerializer.Serialize(stringWriter, new Item());
|
||||
string str = stringWriter.GetStringBuilder().ToString();
|
||||
Item deserialize = jsonSerializer.Deserialize<Item>(new JsonTextReader(new StringReader(str)));
|
||||
|
||||
JToken value = deserialize.ExtensionData["Foo"]["$type"];
|
||||
Assert.AreEqual(JTokenType.String, value.Type);
|
||||
Assert.AreEqual("foo", (string)deserialize.ExtensionData["Foo"]["$values"][0]);
|
||||
Assert.AreEqual("bar", (string)deserialize.ExtensionData["Foo"]["$values"][1]);
|
||||
}
|
||||
|
||||
public class ItemWithConstructor
|
||||
{
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, JToken> ExtensionData;
|
||||
|
||||
public ItemWithConstructor(string temp)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<string> Foo
|
||||
{
|
||||
get { yield return "foo"; yield return "bar"; }
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeserializeWithConstructor_WriteJsonDirectlyToJToken()
|
||||
{
|
||||
JsonSerializer jsonSerializer = new JsonSerializer
|
||||
{
|
||||
TypeNameHandling = TypeNameHandling.Auto
|
||||
};
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
jsonSerializer.Serialize(stringWriter, new ItemWithConstructor(null));
|
||||
string str = stringWriter.GetStringBuilder().ToString();
|
||||
Item deserialize = jsonSerializer.Deserialize<Item>(new JsonTextReader(new StringReader(str)));
|
||||
|
||||
JToken value = deserialize.ExtensionData["Foo"]["$type"];
|
||||
Assert.AreEqual(JTokenType.String, value.Type);
|
||||
Assert.AreEqual("foo", (string)deserialize.ExtensionData["Foo"]["$values"][0]);
|
||||
Assert.AreEqual("bar", (string)deserialize.ExtensionData["Foo"]["$values"][1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -509,7 +509,6 @@ namespace Newtonsoft.Json.Serialization
|
||||
|
||||
Type keyType = dictionaryType.GetGenericArguments()[0];
|
||||
Type valueType = dictionaryType.GetGenericArguments()[1];
|
||||
bool isJTokenValueType = typeof(JToken).IsAssignableFrom(valueType);
|
||||
|
||||
Type createdType;
|
||||
|
||||
@@ -524,61 +523,57 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
|
||||
Func<object, object> getExtensionDataDictionary = JsonTypeReflector.ReflectionDelegateFactory.CreateGet<object>(member);
|
||||
Action<object, object> setExtensionDataDictionary = (ReflectionUtils.CanSetMemberValue(member, true, false))
|
||||
? JsonTypeReflector.ReflectionDelegateFactory.CreateSet<object>(member)
|
||||
: null;
|
||||
Func<object> createExtensionDataDictionary = JsonTypeReflector.ReflectionDelegateFactory.CreateDefaultConstructor<object>(createdType);
|
||||
MethodInfo addMethod = t.GetMethod("Add", new[] { keyType, valueType });
|
||||
MethodCall<object, object> setExtensionDataDictionaryValue = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(addMethod);
|
||||
|
||||
ExtensionDataSetter extensionDataSetter = (o, key, value) =>
|
||||
{
|
||||
object dictionary = getExtensionDataDictionary(o);
|
||||
if (dictionary == null)
|
||||
{
|
||||
if (setExtensionDataDictionary == null)
|
||||
{
|
||||
throw new JsonSerializationException("Cannot set value onto extension data member '{0}'. The extension data collection is null and it cannot be set.".FormatWith(CultureInfo.InvariantCulture, member.Name));
|
||||
}
|
||||
|
||||
dictionary = createExtensionDataDictionary();
|
||||
setExtensionDataDictionary(o, dictionary);
|
||||
}
|
||||
|
||||
// convert object value to JToken so it is compatible with dictionary
|
||||
// could happen because of primitive types, type name handling and references
|
||||
if (isJTokenValueType && !(value is JToken))
|
||||
{
|
||||
value = (value != null) ? JToken.FromObject(value) : JValue.CreateNull();
|
||||
}
|
||||
|
||||
setExtensionDataDictionaryValue(dictionary, key, value);
|
||||
};
|
||||
|
||||
Type enumerableWrapper = typeof(DictionaryEnumerator<,>).MakeGenericType(keyType, valueType);
|
||||
ConstructorInfo constructors = enumerableWrapper.GetConstructors().First();
|
||||
ObjectConstructor<object> createEnumerableWrapper = JsonTypeReflector.ReflectionDelegateFactory.CreateParameterizedConstructor(constructors);
|
||||
|
||||
ExtensionDataGetter extensionDataGetter = o =>
|
||||
{
|
||||
object dictionary = getExtensionDataDictionary(o);
|
||||
if (dictionary == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (IEnumerable<KeyValuePair<object, object>>)createEnumerableWrapper(dictionary);
|
||||
};
|
||||
|
||||
if (extensionDataAttribute.ReadData)
|
||||
{
|
||||
Action<object, object> setExtensionDataDictionary = (ReflectionUtils.CanSetMemberValue(member, true, false))
|
||||
? JsonTypeReflector.ReflectionDelegateFactory.CreateSet<object>(member)
|
||||
: null;
|
||||
Func<object> createExtensionDataDictionary = JsonTypeReflector.ReflectionDelegateFactory.CreateDefaultConstructor<object>(createdType);
|
||||
MethodInfo addMethod = t.GetMethod("Add", new[] { keyType, valueType });
|
||||
MethodCall<object, object> setExtensionDataDictionaryValue = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(addMethod);
|
||||
|
||||
ExtensionDataSetter extensionDataSetter = (o, key, value) =>
|
||||
{
|
||||
object dictionary = getExtensionDataDictionary(o);
|
||||
if (dictionary == null)
|
||||
{
|
||||
if (setExtensionDataDictionary == null)
|
||||
{
|
||||
throw new JsonSerializationException("Cannot set value onto extension data member '{0}'. The extension data collection is null and it cannot be set.".FormatWith(CultureInfo.InvariantCulture, member.Name));
|
||||
}
|
||||
|
||||
dictionary = createExtensionDataDictionary();
|
||||
setExtensionDataDictionary(o, dictionary);
|
||||
}
|
||||
|
||||
setExtensionDataDictionaryValue(dictionary, key, value);
|
||||
};
|
||||
|
||||
contract.ExtensionDataSetter = extensionDataSetter;
|
||||
}
|
||||
|
||||
if (extensionDataAttribute.WriteData)
|
||||
{
|
||||
Type enumerableWrapper = typeof(DictionaryEnumerator<,>).MakeGenericType(keyType, valueType);
|
||||
ConstructorInfo constructors = enumerableWrapper.GetConstructors().First();
|
||||
ObjectConstructor<object> createEnumerableWrapper = JsonTypeReflector.ReflectionDelegateFactory.CreateParameterizedConstructor(constructors);
|
||||
|
||||
ExtensionDataGetter extensionDataGetter = o =>
|
||||
{
|
||||
object dictionary = getExtensionDataDictionary(o);
|
||||
if (dictionary == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (IEnumerable<KeyValuePair<object, object>>)createEnumerableWrapper(dictionary);
|
||||
};
|
||||
|
||||
contract.ExtensionDataGetter = extensionDataGetter;
|
||||
}
|
||||
|
||||
contract.ExtensionDataValueType = valueType;
|
||||
}
|
||||
|
||||
internal struct DictionaryEnumerator<TEnumeratorKey, TEnumeratorValue> : IEnumerable<KeyValuePair<object, object>>, IEnumerator<KeyValuePair<object, object>>
|
||||
|
||||
@@ -28,6 +28,7 @@ using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
|
||||
namespace Newtonsoft.Json.Serialization
|
||||
@@ -144,12 +145,27 @@ namespace Newtonsoft.Json.Serialization
|
||||
/// </summary>
|
||||
public ExtensionDataGetter ExtensionDataGetter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the extension data value type.
|
||||
/// </summary>
|
||||
public Type ExtensionDataValueType
|
||||
{
|
||||
get { return _extensionDataValueType; }
|
||||
set
|
||||
{
|
||||
_extensionDataValueType = value;
|
||||
ExtensionDataIsJToken = (value != null && typeof(JToken).IsAssignableFrom(value));
|
||||
}
|
||||
}
|
||||
|
||||
internal bool ExtensionDataIsJToken;
|
||||
private bool? _hasRequiredOrDefaultValueProperties;
|
||||
private ConstructorInfo _parametrizedConstructor;
|
||||
private ConstructorInfo _overrideConstructor;
|
||||
private ObjectConstructor<object> _overrideCreator;
|
||||
private ObjectConstructor<object> _parameterizedCreator;
|
||||
private JsonPropertyCollection _creatorParameters;
|
||||
private Type _extensionDataValueType;
|
||||
|
||||
internal bool HasRequiredOrDefaultValueProperties
|
||||
{
|
||||
|
||||
@@ -2154,7 +2154,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
|
||||
if (extensionData != null)
|
||||
{
|
||||
object value = CreateValueInternal(reader, null, null, null, contract, containerProperty, null);
|
||||
object value = ReadExtensionDataValue(contract, containerProperty, reader);
|
||||
extensionData[memberName] = value;
|
||||
}
|
||||
else
|
||||
@@ -2433,7 +2433,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
{
|
||||
try
|
||||
{
|
||||
object value = CreateValueInternal(reader, null, null, null, contract, member, null);
|
||||
object value = ReadExtensionDataValue(contract, member, reader);
|
||||
|
||||
contract.ExtensionDataSetter(o, memberName, value);
|
||||
}
|
||||
@@ -2448,6 +2448,20 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
}
|
||||
|
||||
private object ReadExtensionDataValue(JsonObjectContract contract, JsonProperty member, JsonReader reader)
|
||||
{
|
||||
object value;
|
||||
if (contract.ExtensionDataIsJToken)
|
||||
{
|
||||
value = JToken.ReadFrom(reader);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = CreateValueInternal(reader, null, null, null, contract, member, null);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private void EndObject(object newObject, JsonReader reader, JsonObjectContract contract, int initialDepth, Dictionary<JsonProperty, PropertyPresence> propertiesPresence)
|
||||
{
|
||||
if (propertiesPresence != null)
|
||||
|
||||
Reference in New Issue
Block a user