-Improved duplicate object reference id error message
This commit is contained in:
@@ -76,5 +76,5 @@ using System.Security;
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("4.5.0.0")]
|
||||
#if !PocketPC
|
||||
[assembly: AssemblyFileVersion("4.5.9.15313")]
|
||||
[assembly: AssemblyFileVersion("4.5.9.15324")]
|
||||
#endif
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Tests.TestObjects;
|
||||
@@ -1055,6 +1056,27 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
Assert.IsTrue(ReferenceEquals(c2, c3));
|
||||
Assert.IsFalse(ReferenceEquals(c2, c4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DuplicateId()
|
||||
{
|
||||
string json = @"{
|
||||
""Data"": {
|
||||
""Prop1"": {
|
||||
""$id"": ""1"",
|
||||
""MyProperty"": 0
|
||||
},
|
||||
""Prop2"": {
|
||||
""$id"": ""1"",
|
||||
""MyProperty"": 0
|
||||
}
|
||||
}
|
||||
}";
|
||||
|
||||
ExceptionAssert.Throws<JsonSerializationException>(
|
||||
"Error reading object reference '1'. Path 'Data.Prop2.MyProperty', line 9, position 20.",
|
||||
() => JsonConvert.DeserializeObject<PropertyItemIsReferenceObject>(json));
|
||||
}
|
||||
}
|
||||
|
||||
public class PropertyItemIsReferenceBody
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace Newtonsoft.Json.Converters
|
||||
.FormatWith(CultureInfo.InvariantCulture, n2, t.Name));
|
||||
}
|
||||
|
||||
map.Add(n1, n2);
|
||||
map.Set(n1, n2);
|
||||
}
|
||||
|
||||
_enumMemberNamesPerType[t] = map;
|
||||
|
||||
@@ -90,7 +90,7 @@ using System.Security;
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("4.5.0.0")]
|
||||
#if !PocketPC
|
||||
[assembly: AssemblyFileVersion("4.5.9.15313")]
|
||||
[assembly: AssemblyFileVersion("4.5.9.15324")]
|
||||
#endif
|
||||
|
||||
[assembly: CLSCompliant(true)]
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
{
|
||||
_referenceCount++;
|
||||
reference = _referenceCount.ToString(CultureInfo.InvariantCulture);
|
||||
mappings.Add(reference, value);
|
||||
mappings.Set(reference, value);
|
||||
}
|
||||
|
||||
return reference;
|
||||
@@ -71,7 +71,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
|
||||
public void AddReference(object context, string reference, object value)
|
||||
{
|
||||
GetMappings(context).Add(reference, value);
|
||||
GetMappings(context).Set(reference, value);
|
||||
}
|
||||
|
||||
public bool IsReferenced(object context, object value)
|
||||
|
||||
@@ -73,7 +73,9 @@ namespace Newtonsoft.Json.Serialization
|
||||
if (_mappings == null)
|
||||
_mappings = new BidirectionalDictionary<string, object>(
|
||||
EqualityComparer<string>.Default,
|
||||
new ReferenceEqualsEqualityComparer());
|
||||
new ReferenceEqualsEqualityComparer(),
|
||||
"A different value already has the Id '{0}'.",
|
||||
"A different Id has already been assigned for value '{0}'.");
|
||||
|
||||
return _mappings;
|
||||
}
|
||||
|
||||
@@ -761,6 +761,18 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AddReference(JsonReader reader, string id, object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
Serializer.ReferenceResolver.AddReference(this, id, value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw JsonSerializationException.Create(reader, "Error reading object reference '{0}'.".FormatWith(CultureInfo.InvariantCulture, id), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasFlag(DefaultValueHandling value, DefaultValueHandling flag)
|
||||
{
|
||||
return ((value & flag) == flag);
|
||||
@@ -799,7 +811,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
|
||||
object dictionary = wrappedDictionary.UnderlyingDictionary;
|
||||
|
||||
if (id != null)
|
||||
Serializer.ReferenceResolver.AddReference(this, id, dictionary);
|
||||
AddReference(reader, id, dictionary);
|
||||
|
||||
contract.InvokeOnDeserializing(dictionary, Serializer.Context);
|
||||
|
||||
@@ -872,7 +884,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
|
||||
int rank = contract.UnderlyingType.GetArrayRank();
|
||||
|
||||
if (id != null)
|
||||
Serializer.ReferenceResolver.AddReference(this, id, list);
|
||||
AddReference(reader, id, list);
|
||||
|
||||
contract.InvokeOnDeserializing(list, Serializer.Context);
|
||||
|
||||
@@ -1010,7 +1022,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
|
||||
object list = wrappedList.UnderlyingCollection;
|
||||
|
||||
if (id != null)
|
||||
Serializer.ReferenceResolver.AddReference(this, id, list);
|
||||
AddReference(reader, id, list);
|
||||
|
||||
// can't populate an existing array
|
||||
if (wrappedList.IsFixedSize)
|
||||
@@ -1137,7 +1149,7 @@ To fix this error either change the environment to be fully trusted, change the
|
||||
object createdObject = contract.ISerializableCreator(serializationInfo, Serializer.Context);
|
||||
|
||||
if (id != null)
|
||||
Serializer.ReferenceResolver.AddReference(this, id, createdObject);
|
||||
AddReference(reader, id, createdObject);
|
||||
|
||||
// these are together because OnDeserializing takes an object but for an ISerializable the object is fully created in the constructor
|
||||
contract.InvokeOnDeserializing(createdObject, Serializer.Context);
|
||||
@@ -1162,7 +1174,7 @@ To fix this error either change the environment to be fully trusted, change the
|
||||
throw JsonSerializationException.Create(reader, "Unable to find a default constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
|
||||
|
||||
if (id != null)
|
||||
Serializer.ReferenceResolver.AddReference(this, id, newObject);
|
||||
AddReference(reader, id, newObject);
|
||||
|
||||
contract.InvokeOnDeserializing(newObject, Serializer.Context);
|
||||
|
||||
@@ -1257,7 +1269,7 @@ To fix this error either change the environment to be fully trusted, change the
|
||||
object createdObject = constructorInfo.Invoke(constructorParameters.Values.ToArray());
|
||||
|
||||
if (id != null)
|
||||
Serializer.ReferenceResolver.AddReference(this, id, createdObject);
|
||||
AddReference(reader, id, createdObject);
|
||||
|
||||
contract.InvokeOnDeserializing(createdObject, Serializer.Context);
|
||||
|
||||
@@ -1478,7 +1490,7 @@ To fix this error either change the environment to be fully trusted, change the
|
||||
: null;
|
||||
|
||||
if (id != null)
|
||||
Serializer.ReferenceResolver.AddReference(this, id, newObject);
|
||||
AddReference(reader, id, newObject);
|
||||
|
||||
int initialDepth = reader.Depth;
|
||||
|
||||
|
||||
@@ -254,10 +254,23 @@ namespace Newtonsoft.Json.Serialization
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName(JsonTypeReflector.RefPropertyName);
|
||||
writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, value));
|
||||
writer.WriteValue(GetReference(writer, value));
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
private string GetReference(JsonWriter writer, object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
string reference = Serializer.ReferenceResolver.GetReference(this, value);
|
||||
return reference;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw JsonSerializationException.Create(null, writer.ContainerPath, "Error writing object reference for '{0}'.".FormatWith(CultureInfo.InvariantCulture, value.GetType()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool TryConvertToString(object value, Type type, out string s)
|
||||
{
|
||||
#if !(PocketPC || NETFX_CORE || PORTABLE)
|
||||
@@ -397,7 +410,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
if (isReference)
|
||||
{
|
||||
writer.WritePropertyName(JsonTypeReflector.IdPropertyName);
|
||||
writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, value));
|
||||
writer.WriteValue(GetReference(writer, value));
|
||||
}
|
||||
if (ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionContract, containerProperty))
|
||||
{
|
||||
@@ -584,7 +597,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
if (isReference)
|
||||
{
|
||||
writer.WritePropertyName(JsonTypeReflector.IdPropertyName);
|
||||
writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, values));
|
||||
writer.WriteValue(GetReference(writer, values));
|
||||
}
|
||||
if (includeTypeDetails)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Newtonsoft.Json.Utilities
|
||||
{
|
||||
@@ -32,6 +33,8 @@ namespace Newtonsoft.Json.Utilities
|
||||
{
|
||||
private readonly IDictionary<TFirst, TSecond> _firstToSecond;
|
||||
private readonly IDictionary<TSecond, TFirst> _secondToFirst;
|
||||
private readonly string _duplicateFirstErrorMessage;
|
||||
private readonly string _duplicateSecondErrorMessage;
|
||||
|
||||
public BidirectionalDictionary()
|
||||
: this(EqualityComparer<TFirst>.Default, EqualityComparer<TSecond>.Default)
|
||||
@@ -39,17 +42,40 @@ namespace Newtonsoft.Json.Utilities
|
||||
}
|
||||
|
||||
public BidirectionalDictionary(IEqualityComparer<TFirst> firstEqualityComparer, IEqualityComparer<TSecond> secondEqualityComparer)
|
||||
: this(
|
||||
firstEqualityComparer,
|
||||
secondEqualityComparer,
|
||||
"Duplicate item already exists for '{0}'.",
|
||||
"Duplicate item already exists for '{0}'.")
|
||||
{
|
||||
}
|
||||
|
||||
public BidirectionalDictionary(IEqualityComparer<TFirst> firstEqualityComparer, IEqualityComparer<TSecond> secondEqualityComparer,
|
||||
string duplicateFirstErrorMessage, string duplicateSecondErrorMessage)
|
||||
{
|
||||
_firstToSecond = new Dictionary<TFirst, TSecond>(firstEqualityComparer);
|
||||
_secondToFirst = new Dictionary<TSecond, TFirst>(secondEqualityComparer);
|
||||
_duplicateFirstErrorMessage = duplicateFirstErrorMessage;
|
||||
_duplicateSecondErrorMessage = duplicateSecondErrorMessage;
|
||||
}
|
||||
|
||||
public void Add(TFirst first, TSecond second)
|
||||
public void Set(TFirst first, TSecond second)
|
||||
{
|
||||
if (_firstToSecond.ContainsKey(first) || _secondToFirst.ContainsKey(second))
|
||||
TFirst existingFirst;
|
||||
TSecond existingSecond;
|
||||
|
||||
if (_firstToSecond.TryGetValue(first, out existingSecond))
|
||||
{
|
||||
throw new ArgumentException("Duplicate first or second");
|
||||
if (!existingSecond.Equals(second))
|
||||
throw new ArgumentException(_duplicateFirstErrorMessage.FormatWith(CultureInfo.InvariantCulture, first));
|
||||
}
|
||||
|
||||
if (_secondToFirst.TryGetValue(second, out existingFirst))
|
||||
{
|
||||
if (!existingFirst.Equals(first))
|
||||
throw new ArgumentException(_duplicateSecondErrorMessage.FormatWith(CultureInfo.InvariantCulture, second));
|
||||
}
|
||||
|
||||
_firstToSecond.Add(first, second);
|
||||
_secondToFirst.Add(second, first);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user