-Added EqualityComparer to JsonSerializer

This commit is contained in:
James Newton-King
2015-06-14 13:40:06 +12:00
parent 70521eea88
commit 3cc797c240
8 changed files with 133 additions and 4 deletions
@@ -798,6 +798,9 @@ namespace Newtonsoft.Json.Tests.Serialization
serializer.Culture = new CultureInfo("en-nz");
Assert.AreEqual("en-NZ", serializer.Culture.ToString());
serializer.EqualityComparer = EqualityComparer<object>.Default;
Assert.AreEqual(EqualityComparer<object>.Default, serializer.EqualityComparer);
serializer.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
Assert.AreEqual(DateFormatHandling.MicrosoftDateFormat, serializer.DateFormatHandling);
@@ -890,6 +893,9 @@ namespace Newtonsoft.Json.Tests.Serialization
settings.Culture = new CultureInfo("en-nz");
Assert.AreEqual("en-NZ", settings.Culture.ToString());
settings.EqualityComparer = EqualityComparer<object>.Default;
Assert.AreEqual(EqualityComparer<object>.Default, settings.EqualityComparer);
settings.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
Assert.AreEqual(DateFormatHandling.MicrosoftDateFormat, settings.DateFormatHandling);
@@ -988,6 +994,9 @@ namespace Newtonsoft.Json.Tests.Serialization
serializerProxy.Culture = new CultureInfo("en-nz");
Assert.AreEqual("en-NZ", serializerProxy.Culture.ToString());
serializerProxy.EqualityComparer = EqualityComparer<object>.Default;
Assert.AreEqual(EqualityComparer<object>.Default, serializerProxy.EqualityComparer);
serializerProxy.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
Assert.AreEqual(DateFormatHandling.MicrosoftDateFormat, serializerProxy.DateFormatHandling);
@@ -24,7 +24,9 @@
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
#if NETFX_CORE
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute;
@@ -268,6 +270,80 @@ namespace Newtonsoft.Json.Tests.Serialization
Assert.AreEqual(@"{""child"":{""name"":""child""},""name"":""parent""}", c);
}
#endif
[Test]
public void EqualityComparer()
{
AccountWithEquals account = new AccountWithEquals
{
Name = "main"
};
AccountWithEquals manager = new AccountWithEquals
{
Name = "main"
};
account.Manager = manager;
ExceptionAssert.Throws<JsonSerializationException>(
() => JsonConvert.SerializeObject(account),
"Self referencing loop detected for property 'Manager' with type 'Newtonsoft.Json.Tests.Serialization.AccountWithEquals'. Path ''.");
string json = JsonConvert.SerializeObject(account, new JsonSerializerSettings
{
EqualityComparer = new ReferenceEqualsEqualityComparer(),
Formatting = Formatting.Indented
});
StringAssert.AreEqual(@"{
""Name"": ""main"",
""Manager"": {
""Name"": ""main"",
""Manager"": null
}
}", json);
}
}
public class ReferenceEqualsEqualityComparer : IEqualityComparer
{
bool IEqualityComparer.Equals(object x, object y)
{
return ReferenceEquals(x, y);
}
int IEqualityComparer.GetHashCode(object obj)
{
#if !(NETFX_CORE)
// 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
}
}
public class AccountWithEquals
{
public string Name { get; set; }
public AccountWithEquals Manager { get; set; }
public override bool Equals(object obj)
{
AccountWithEquals a = obj as AccountWithEquals;
if (a == null)
return false;
return Name == a.Name;
}
public override int GetHashCode()
{
if (Name == null)
return 0;
return Name.GetHashCode();
}
}
public class PropertyItemReferenceLoopHandling
+14
View File
@@ -24,6 +24,7 @@
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
@@ -56,6 +57,7 @@ namespace Newtonsoft.Json
internal JsonConverterCollection _converters;
internal IContractResolver _contractResolver;
internal ITraceWriter _traceWriter;
internal IEqualityComparer _equalityComparer;
internal SerializationBinder _binder;
internal StreamingContext _context;
private IReferenceResolver _referenceResolver;
@@ -119,6 +121,16 @@ namespace Newtonsoft.Json
set { _traceWriter = value; }
}
/// <summary>
/// Gets or sets the equality comparer used by the serializer when comparing references.
/// </summary>
/// <value>The equality comparer.</value>
public virtual IEqualityComparer EqualityComparer
{
get { return _equalityComparer; }
set { _equalityComparer = value; }
}
/// <summary>
/// Gets or sets how type name writing and reading is handled by the serializer.
/// </summary>
@@ -563,6 +575,8 @@ namespace Newtonsoft.Json
serializer.ReferenceResolver = settings.ReferenceResolverProvider();
if (settings.TraceWriter != null)
serializer.TraceWriter = settings.TraceWriter;
if (settings.EqualityComparer != null)
serializer.EqualityComparer = settings.EqualityComparer;
if (settings.Binder != null)
serializer.Binder = settings.Binder;
@@ -24,6 +24,7 @@
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.Serialization.Formatters;
@@ -199,6 +200,12 @@ namespace Newtonsoft.Json
/// <value>The contract resolver.</value>
public IContractResolver ContractResolver { get; set; }
/// <summary>
/// Gets or sets the equality comparer used by the serializer when comparing references.
/// </summary>
/// <value>The equality comparer.</value>
public IEqualityComparer EqualityComparer { get; set; }
/// <summary>
/// Gets or sets the <see cref="IReferenceResolver"/> used by the serializer when resolving references.
/// </summary>
@@ -47,8 +47,8 @@ namespace Newtonsoft.Json.Serialization
// 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;
// put all objects in the same bucket so ReferenceEquals is called on all
return -1;
#endif
}
}
@@ -272,7 +272,11 @@ namespace Newtonsoft.Json.Serialization
if (referenceLoopHandling == null && containerContract != null)
referenceLoopHandling = containerContract.ItemReferenceLoopHandling;
if (_serializeStack.IndexOf(value) != -1)
bool exists = (Serializer._equalityComparer != null)
? _serializeStack.Contains(value, Serializer._equalityComparer)
: _serializeStack.Contains(value);
if (exists)
{
string message = "Self referencing loop detected";
if (property != null)
@@ -24,6 +24,7 @@
#endregion
using System;
using System.Collections;
using System.Globalization;
using System.Runtime.Serialization.Formatters;
using Newtonsoft.Json.Utilities;
@@ -55,6 +56,12 @@ namespace Newtonsoft.Json.Serialization
set { _serializer.TraceWriter = value; }
}
public override IEqualityComparer EqualityComparer
{
get { return _serializer.EqualityComparer; }
set { _serializer.EqualityComparer = value; }
}
public override JsonConverterCollection Converters
{
get { return _serializer.Converters; }
@@ -190,8 +190,20 @@ namespace Newtonsoft.Json.Utilities
return -1;
}
public static bool Contains(this IEnumerable list, object value, IEqualityComparer comparer)
{
foreach (object item in list)
{
if (comparer.Equals(item, value))
{
return true;
}
}
return false;
}
/// <summary>
/// Returns the index of the first occurrence in a sequence by using a specified IEqualityComparer.
/// Returns the index of the first occurrence in a sequence by using a specified IEqualityComparer{TSource}.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <param name="list">A sequence in which to locate a value.</param>