-Fixed error when reusing JsonSerializer with preserve references enabled
This commit is contained in:
@@ -4203,5 +4203,20 @@ keyword such as type of business.""
|
||||
Assert.AreEqual(44.4, p.Height);
|
||||
Assert.AreEqual(4d, p.Price);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Error converting value {null} to type 'System.DateTime'.")]
|
||||
public void DeserializeNullDateTimeValueTest()
|
||||
{
|
||||
JsonConvert.DeserializeObject("null", typeof(DateTime));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeserializeNullNullableDateTimeValueTest()
|
||||
{
|
||||
object dateTime = JsonConvert.DeserializeObject("null", typeof(DateTime?));
|
||||
|
||||
Assert.IsNull(dateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -283,7 +284,7 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
{
|
||||
CircularReferenceClass circularReferenceClass = (CircularReferenceClass)value;
|
||||
|
||||
string reference = serializer.ReferenceResolver.GetReference(circularReferenceClass);
|
||||
string reference = serializer.ReferenceResolver.GetReference(serializer, circularReferenceClass);
|
||||
|
||||
JObject me = new JObject();
|
||||
me["$id"] = new JValue(reference);
|
||||
@@ -309,7 +310,7 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
else
|
||||
{
|
||||
string reference = (string)o["$ref"];
|
||||
return serializer.ReferenceResolver.ResolveReference(reference);
|
||||
return serializer.ReferenceResolver.ResolveReference(serializer, reference);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,5 +803,61 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
User deserializedUser = JsonConvert.DeserializeObject<User>(json, serializerSettings);
|
||||
Assert.IsNotNull(deserializedUser);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PreserveReferencesHandlingWithReusedJsonSerializer()
|
||||
{
|
||||
MyClass c = new MyClass();
|
||||
|
||||
IList<MyClass> myClasses1 = new List<MyClass>
|
||||
{
|
||||
c,
|
||||
c
|
||||
};
|
||||
|
||||
var ser = new JsonSerializer()
|
||||
{
|
||||
PreserveReferencesHandling = PreserveReferencesHandling.All
|
||||
};
|
||||
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
||||
using (var sw = new StreamWriter(ms))
|
||||
using (var writer = new JsonTextWriter(sw) { Formatting = Formatting.Indented })
|
||||
{
|
||||
ser.Serialize(writer, myClasses1);
|
||||
}
|
||||
|
||||
byte[] data = ms.ToArray();
|
||||
string json = Encoding.UTF8.GetString(data, 0, data.Length);
|
||||
|
||||
Assert.AreEqual(@"{
|
||||
""$id"": ""1"",
|
||||
""$values"": [
|
||||
{
|
||||
""$id"": ""2"",
|
||||
""PreProperty"": 0,
|
||||
""PostProperty"": 0
|
||||
},
|
||||
{
|
||||
""$ref"": ""2""
|
||||
}
|
||||
]
|
||||
}", json);
|
||||
|
||||
ms = new MemoryStream(data);
|
||||
IList<MyClass> myClasses2;
|
||||
|
||||
using (var sr = new StreamReader(ms))
|
||||
using (var reader = new JsonTextReader(sr))
|
||||
{
|
||||
myClasses2 = ser.Deserialize<IList<MyClass>>(reader);
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, myClasses2.Count);
|
||||
Assert.AreEqual(myClasses2[0], myClasses2[1]);
|
||||
|
||||
Assert.AreNotEqual(myClasses1[0], myClasses2[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,72 +35,53 @@ namespace Newtonsoft.Json.Serialization
|
||||
{
|
||||
internal class DefaultReferenceResolver : IReferenceResolver
|
||||
{
|
||||
private class ReferenceEqualsEqualityComparer : IEqualityComparer<object>
|
||||
{
|
||||
bool IEqualityComparer<object>.Equals(object x, object y)
|
||||
{
|
||||
return ReferenceEquals(x, y);
|
||||
}
|
||||
|
||||
int IEqualityComparer<object>.GetHashCode(object obj)
|
||||
{
|
||||
#if !PocketPC
|
||||
// put objects in a bucket based on their reference
|
||||
return RuntimeHelpers.GetHashCode(obj);
|
||||
#else
|
||||
// put all objects in the same bucket so ReferenceEquals is called on all
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private int _referenceCount;
|
||||
private BidirectionalDictionary<string, object> _mappings;
|
||||
|
||||
private BidirectionalDictionary<string, object> Mappings
|
||||
private BidirectionalDictionary<string, object> GetMappings(object context)
|
||||
{
|
||||
get
|
||||
{
|
||||
// override equality comparer for object key dictionary
|
||||
// object will be modified as it deserializes and might have mutable hashcode
|
||||
if (_mappings == null)
|
||||
_mappings = new BidirectionalDictionary<string, object>(
|
||||
EqualityComparer<string>.Default,
|
||||
new ReferenceEqualsEqualityComparer());
|
||||
JsonSerializerInternalBase internalSerializer;
|
||||
|
||||
return _mappings;
|
||||
}
|
||||
if (context is JsonSerializerInternalBase)
|
||||
internalSerializer = (JsonSerializerInternalBase) context;
|
||||
else if (context is JsonSerializerProxy)
|
||||
internalSerializer = ((JsonSerializerProxy) context).GetInternalSerializer();
|
||||
else
|
||||
throw new Exception("The DefaultReferenceResolver can only be used internally.");
|
||||
|
||||
return internalSerializer.DefaultReferenceMappings;
|
||||
}
|
||||
|
||||
public object ResolveReference(string reference)
|
||||
public object ResolveReference(object context, string reference)
|
||||
{
|
||||
object value;
|
||||
Mappings.TryGetByFirst(reference, out value);
|
||||
GetMappings(context).TryGetByFirst(reference, out value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public string GetReference(object value)
|
||||
public string GetReference(object context, object value)
|
||||
{
|
||||
var mappings = GetMappings(context);
|
||||
|
||||
string reference;
|
||||
if (!Mappings.TryGetBySecond(value, out reference))
|
||||
if (!mappings.TryGetBySecond(value, out reference))
|
||||
{
|
||||
_referenceCount++;
|
||||
reference = _referenceCount.ToString(CultureInfo.InvariantCulture);
|
||||
Mappings.Add(reference, value);
|
||||
mappings.Add(reference, value);
|
||||
}
|
||||
|
||||
return reference;
|
||||
}
|
||||
|
||||
public void AddReference(string reference, object value)
|
||||
public void AddReference(object context, string reference, object value)
|
||||
{
|
||||
Mappings.Add(reference, value);
|
||||
GetMappings(context).Add(reference, value);
|
||||
}
|
||||
|
||||
public bool IsReferenced(object value)
|
||||
public bool IsReferenced(object context, object value)
|
||||
{
|
||||
string reference;
|
||||
return Mappings.TryGetBySecond(value, out reference);
|
||||
return GetMappings(context).TryGetBySecond(value, out reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,28 +33,32 @@ namespace Newtonsoft.Json.Serialization
|
||||
/// <summary>
|
||||
/// Resolves a reference to its object.
|
||||
/// </summary>
|
||||
/// <param name="context">The serialization context.</param>
|
||||
/// <param name="reference">The reference to resolve.</param>
|
||||
/// <returns>The object that</returns>
|
||||
object ResolveReference(string reference);
|
||||
object ResolveReference(object context, string reference);
|
||||
/// <summary>
|
||||
/// Gets the reference for the sepecified object.
|
||||
/// </summary>
|
||||
/// <param name="context">The serialization context.</param>
|
||||
/// <param name="value">The object to get a reference for.</param>
|
||||
/// <returns>The reference to the object.</returns>
|
||||
string GetReference(object value);
|
||||
string GetReference(object context, object value);
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is referenced.
|
||||
/// </summary>
|
||||
/// <param name="context">The serialization context.</param>
|
||||
/// <param name="value">The object to test for a reference.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified object is referenced; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
bool IsReferenced(object value);
|
||||
bool IsReferenced(object context, object value);
|
||||
/// <summary>
|
||||
/// Adds a reference to the specified object.
|
||||
/// </summary>
|
||||
/// <param name="context">The serialization context.</param>
|
||||
/// <param name="reference">The reference.</param>
|
||||
/// <param name="value">The object to reference.</param>
|
||||
void AddReference(string reference, object value);
|
||||
void AddReference(object context, string reference, object value);
|
||||
}
|
||||
}
|
||||
@@ -24,14 +24,36 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
|
||||
namespace Newtonsoft.Json.Serialization
|
||||
{
|
||||
internal abstract class JsonSerializerInternalBase
|
||||
{
|
||||
private class ReferenceEqualsEqualityComparer : IEqualityComparer<object>
|
||||
{
|
||||
bool IEqualityComparer<object>.Equals(object x, object y)
|
||||
{
|
||||
return ReferenceEquals(x, y);
|
||||
}
|
||||
|
||||
int IEqualityComparer<object>.GetHashCode(object obj)
|
||||
{
|
||||
#if !PocketPC
|
||||
// put objects in a bucket based on their reference
|
||||
return RuntimeHelpers.GetHashCode(obj);
|
||||
#else
|
||||
// put all objects in the same bucket so ReferenceEquals is called on all
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private ErrorContext _currentErrorContext;
|
||||
private BidirectionalDictionary<string, object> _mappings;
|
||||
|
||||
internal JsonSerializer Serializer { get; private set; }
|
||||
|
||||
@@ -42,6 +64,21 @@ namespace Newtonsoft.Json.Serialization
|
||||
Serializer = serializer;
|
||||
}
|
||||
|
||||
internal BidirectionalDictionary<string, object> DefaultReferenceMappings
|
||||
{
|
||||
get
|
||||
{
|
||||
// override equality comparer for object key dictionary
|
||||
// object will be modified as it deserializes and might have mutable hashcode
|
||||
if (_mappings == null)
|
||||
_mappings = new BidirectionalDictionary<string, object>(
|
||||
EqualityComparer<string>.Default,
|
||||
new ReferenceEqualsEqualityComparer());
|
||||
|
||||
return _mappings;
|
||||
}
|
||||
}
|
||||
|
||||
protected ErrorContext GetErrorContext(object currentObject, object member, Exception error)
|
||||
{
|
||||
if (_currentErrorContext == null)
|
||||
|
||||
@@ -315,7 +315,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
if (reader.TokenType == JsonToken.PropertyName)
|
||||
throw new JsonSerializationException("Additional content found in JSON reference object. A JSON reference object should only have a {0} property.".FormatWith(CultureInfo.InvariantCulture, JsonTypeReflector.RefPropertyName));
|
||||
|
||||
return Serializer.ReferenceResolver.ResolveReference(reference);
|
||||
return Serializer.ReferenceResolver.ResolveReference(this, reference);
|
||||
}
|
||||
else if (string.Equals(propertyName, JsonTypeReflector.TypePropertyName, StringComparison.Ordinal))
|
||||
{
|
||||
@@ -598,7 +598,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
private object PopulateDictionary(IWrappedDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, string id)
|
||||
{
|
||||
if (id != null)
|
||||
Serializer.ReferenceResolver.AddReference(id, dictionary.UnderlyingDictionary);
|
||||
Serializer.ReferenceResolver.AddReference(this, id, dictionary.UnderlyingDictionary);
|
||||
|
||||
contract.InvokeOnDeserializing(dictionary.UnderlyingDictionary, Serializer.Context);
|
||||
|
||||
@@ -686,7 +686,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
object list = wrappedList.UnderlyingCollection;
|
||||
|
||||
if (reference != null)
|
||||
Serializer.ReferenceResolver.AddReference(reference, list);
|
||||
Serializer.ReferenceResolver.AddReference(this, reference, list);
|
||||
|
||||
contract.InvokeOnDeserializing(list, Serializer.Context);
|
||||
|
||||
@@ -756,7 +756,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
object createdObject = contract.ISerializableCreator(serializationInfo, Serializer.Context);
|
||||
|
||||
if (id != null)
|
||||
Serializer.ReferenceResolver.AddReference(id, createdObject);
|
||||
Serializer.ReferenceResolver.AddReference(this, id, createdObject);
|
||||
|
||||
// these are together because OnDeserializing takes an object but for an ISerializable the object is full created in the constructor
|
||||
contract.InvokeOnDeserializing(createdObject, Serializer.Context);
|
||||
@@ -781,7 +781,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
throw new JsonSerializationException("Unable to find a default constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
|
||||
|
||||
if (id != null)
|
||||
Serializer.ReferenceResolver.AddReference(id, newObject);
|
||||
Serializer.ReferenceResolver.AddReference(this, id, newObject);
|
||||
|
||||
contract.InvokeOnDeserializing(newObject, Serializer.Context);
|
||||
|
||||
@@ -920,7 +920,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
object createdObject = constructorInfo.Invoke(constructorParameters.Values.ToArray());
|
||||
|
||||
if (id != null)
|
||||
Serializer.ReferenceResolver.AddReference(id, createdObject);
|
||||
Serializer.ReferenceResolver.AddReference(this, id, createdObject);
|
||||
|
||||
contract.InvokeOnDeserializing(createdObject, Serializer.Context);
|
||||
|
||||
@@ -976,7 +976,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
contract.Properties.Where(m => m.Required != Required.Default).ToDictionary(m => m, m => RequiredValue.None);
|
||||
|
||||
if (id != null)
|
||||
Serializer.ReferenceResolver.AddReference(id, newObject);
|
||||
Serializer.ReferenceResolver.AddReference(this, id, newObject);
|
||||
|
||||
int initialDepth = reader.Depth;
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
if (!isReference.Value)
|
||||
return false;
|
||||
|
||||
return Serializer.ReferenceResolver.IsReferenced(value);
|
||||
return Serializer.ReferenceResolver.IsReferenced(this, value);
|
||||
}
|
||||
|
||||
private void WriteMemberInfoProperty(JsonWriter writer, object memberValue, JsonProperty property, JsonContract contract)
|
||||
@@ -231,7 +231,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName(JsonTypeReflector.RefPropertyName);
|
||||
writer.WriteValue(Serializer.ReferenceResolver.GetReference(value));
|
||||
writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, value));
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
if (isReference)
|
||||
{
|
||||
writer.WritePropertyName(JsonTypeReflector.IdPropertyName);
|
||||
writer.WriteValue(Serializer.ReferenceResolver.GetReference(value));
|
||||
writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, value));
|
||||
}
|
||||
if (ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionValueContract))
|
||||
{
|
||||
@@ -386,7 +386,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
if (isReference)
|
||||
{
|
||||
writer.WritePropertyName(JsonTypeReflector.IdPropertyName);
|
||||
writer.WriteValue(Serializer.ReferenceResolver.GetReference(values.UnderlyingCollection));
|
||||
writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, values.UnderlyingCollection));
|
||||
}
|
||||
if (includeTypeDetails)
|
||||
{
|
||||
@@ -539,7 +539,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
if (isReference)
|
||||
{
|
||||
writer.WritePropertyName(JsonTypeReflector.IdPropertyName);
|
||||
writer.WriteValue(Serializer.ReferenceResolver.GetReference(values.UnderlyingDictionary));
|
||||
writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, values.UnderlyingDictionary));
|
||||
}
|
||||
if (ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionValueContract))
|
||||
{
|
||||
|
||||
@@ -125,6 +125,14 @@ namespace Newtonsoft.Json.Serialization
|
||||
set { _serializer.Context = value; }
|
||||
}
|
||||
|
||||
internal JsonSerializerInternalBase GetInternalSerializer()
|
||||
{
|
||||
if (_serializerReader != null)
|
||||
return _serializerReader;
|
||||
else
|
||||
return _serializerWriter;
|
||||
}
|
||||
|
||||
public JsonSerializerProxy(JsonSerializerInternalReader serializerReader)
|
||||
{
|
||||
ValidationUtils.ArgumentNotNull(serializerReader, "serializerReader");
|
||||
|
||||
Reference in New Issue
Block a user