-Improved duplicate object reference id error message

This commit is contained in:
James Newton-King
2012-09-24 21:12:42 +12:00
parent 93679ec415
commit 0bf5f27f2f
9 changed files with 94 additions and 19 deletions
@@ -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);
}