JSON.NET Serialization - How does DefaultReferenceResolver compare equality? - c#

I am using JSON.NET 6.0.3. I have changed PreserveReferences option as follows:
HttpConfiguration.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
My object graph resembles the following:
public class CarFromManufacturer
{
public int CarID { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public CarManufacturer Manufacturer { get; set; }
}
public class CarManufacturer
{
public int ManufacturerID { get; set; }
public string Name { get; set; }
}
My WebAPI controller is returning the result set of IEnumerable[CarFromManufacturer]. So the result could be a list of 5 cars from two unique manufacturer objects. I am expecting the JSON result to list each manufacturer only once fully serialized and then subsequent uses of the same Manufacturer to be $ref ID to the original's $id. That is not happening.
Even though I can't find a single piece of documentation that speaks about how equality is established for the ReferenceResolver, I've implemented IEquatable<CarManufacturer> along with override of base.Equals and base.GetHashCode() with no luck.
I'd like to avoid implementing my own IReferenceResolver because have very similar object graphs working as expected in the same project.
The only thing I can think of is that I am using factory objects and instead of creating each unique CarManufacturer first, then creating the instances of CarFromManufacturer passing in CarManufacturer... i am creating a new instance of the CarManufacturer. This would explain why the objects aren't equal, but that's why I implemented IEquatable and overrides of base.Equals(object) and base.GetHashCode().
I've looked into the source for DefaultReferenceResolver and it uses the default constructor of BidirectionalDictionary which uses EqualityComparer<T>.Default which, from MSDN documentation, uses the T's implementation of IEquatable<T> if it exists, or otherwise uses T's base.Equals() implementation.... all of this would lead me to believe that IEquatable in CarManufacturer should fix my problem. However, placing breakpoints in CarManufacturer.Equals() and GethashCode() never hit..

JSON.NET's logic for resolving references by default just compares references using this comparer.
If you want to compare objects in a different manner, you'll have to implement a custom IReferenceResolver.
Here's an example that takes an IEqualityComparer<T> to accommodate your use case:
public class ReferenceResolver<T> : IReferenceResolver
{
private Dictionary<string, T> stringToReference;
private Dictionary<T, string> referenceToString;
private int referenceCount;
public ReferenceResolver(IEqualityComparer<T> comparer)
{
this.stringToReference = new Dictionary<string, T>();
this.referenceToString = new Dictionary<T, string>(comparer);
this.referenceCount = 0;
}
public void AddReference(
object context,
string reference,
object value)
{
this.referenceToString.Add((T)value, reference);
this.stringToReference.Add(reference, (T)value);
}
public string GetReference(
object context,
object value)
{
string result = null;
if (!this.referenceToString.TryGetValue((T)value, out result))
{
referenceCount++;
result = referenceCount.ToString(CultureInfo.InvariantCulture);
this.referenceToString.Add((T)value, result);
this.stringToReference.Add(result, (T)value);
}
return result;
}
public bool IsReferenced(
object context,
object value)
{
return this.referenceToString.ContainsKey((T)value);
}
public object ResolveReference(
object context,
string reference)
{
T r = default(T);
this.stringToReference.TryGetValue(reference, out r);
return r;
}
}

Json.Net will call the Equals method on the objects being compared. In certain scenarios you may not want this however for example when it is checking for circular references it does the same whereas it may be more ideal to check for reference equality. They do this however to give the developer full control by overridding the Equals method in their classes.
You can override the default implementation. For example to make this a reference equality you would do the following:
var settings = new JsonSerializerSettings
{
EqualityComparer = new DefaultEqualityComparer(),
};
public class DefaultEqualityComparer : IEqualityComparer
{
public bool Equals(object x, object y)
{
return ReferenceEquals(x, y);
}
public int GetHashCode(object obj)
{
return obj.GetHashCode();
}
}

Related

Why ICollection<>.Contains ignores my overridden Equals and the IEquatable<> interface?

I have an issue with a navigation property in an entity framework project.
Here is the class MobileUser:
[DataContract]
[Table("MobileUser")]
public class MobileUser: IEquatable<MobileUser>
{
// constructors omitted....
/// <summary>
/// The primary-key of MobileUser.
/// This is not the VwdId which is stored in a separate column
/// </summary>
[DataMember, Key, Required, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[DataMember, Required, Index(IsUnique = true), MinLength(VwdIdMinLength), MaxLength(VwdIdMaxLength)]
public string VwdId { get; set; }
// other properties omitted ...
[DataMember]
public virtual ICollection<MobileDeviceInfo> DeviceInfos { get; private set; }
public bool Equals(MobileUser other)
{
return this.UserId == other?.UserId || this.VwdId == other?.VwdId;
}
public override bool Equals(object obj)
{
if(object.ReferenceEquals(this, obj))return true;
MobileUser other = obj as MobileUser;
if (other == null) return false;
return this.Equals(other);
}
public override int GetHashCode()
{
// ReSharper disable once NonReadonlyMemberInGetHashCode
return VwdId.GetHashCode();
}
public override string ToString()
{
return "foo"; // omitted actual implementation
}
#region constants
// irrelevant
#endregion
}
The relevant part is this navigation property:
public virtual ICollection<MobileDeviceInfo> DeviceInfos { get; private set; }
This is the class MobileDeviceInfo:
[DataContract]
[Table("MobileDeviceInfo")]
public class MobileDeviceInfo : IEquatable<MobileDeviceInfo>
{
[DataContract]
public enum MobilePlatform
{
[EnumMember]
// ReSharper disable once InconsistentNaming because correct spelling is iOS
iOS = 1,
[EnumMember] Android = 2,
[EnumMember] WindowsPhone = 3,
[EnumMember] Blackberry = 4
}
// constructors omitted ...
[DataMember, Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int DeviceInfoId { get; private set; }
[DataMember, Required, Index(IsUnique = true), MinLength(DeviceTokenMinLength), MaxLength(DeviceTokenMaxLength)]
public string DeviceToken { get; set; }
[DataMember, Required, MinLength(DeviceNameMinLength), MaxLength(DeviceNameMaxLength)]
public string DeviceName { get; set; }
[DataMember, Required]
public MobilePlatform Platform { get; set; }
// other properties ...
[DataMember]
public virtual MobileUser MobileUser { get; private set; }
/// <summary>
/// The foreign-key to the MobileUser.
/// This is not the VwdId which is stored in MobileUser
/// </summary>
[DataMember, ForeignKey("MobileUser")]
public int UserId { get; set; }
public bool Equals(MobileDeviceInfo other)
{
if (other == null) return false;
return DeviceToken == other.DeviceToken;
}
public override string ToString()
{
return "Bah"; // implementation omitted
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj)) return true;
MobileDeviceInfo other = obj as MobileDeviceInfo;
if (other == null) return false;
return Equals(other);
}
public override int GetHashCode()
{
// ReSharper disable once NonReadonlyMemberInGetHashCode
return DeviceToken.GetHashCode();
}
#region constants
// irrelevant
#endregion
}
As you can see, it implements IEquatable<MobileDeviceInfo> and overrides also Equals and GetHashCode from System.Object.
I have following test, i've expected that Contains would call my Equals but it does not. It seems to use Object.ReferenceEquals instead, so won't find my device because it's a different reference:
var userRepo = new MobileUserRepository((ILog)null);
var deviceRepo = new MobileDeviceRepository((ILog)null);
IReadOnlyList<MobileUser> allUser = userRepo.GetAllMobileUsersWithDevices();
MobileUser user = allUser.First();
IReadOnlyList<MobileDeviceInfo> allDevices = deviceRepo.GetMobileDeviceInfos(user.VwdId, true);
MobileDeviceInfo device = allDevices.First();
bool contains = user.DeviceInfos.Contains(device);
bool anyEqual = user.DeviceInfos.Any(x => x.DeviceToken == device.DeviceToken);
Assert.IsTrue(contains); // no, it's false
The second approach with LINQ's Enumerable.Any returns the expected true.
If i don't use user.DeviceInfos.Contains(device) but user.DeviceInfos.ToList().Contains(device) it also works as expected since List<>.Contains uses my Equals.
The actual type of the ICollection<> seems to be a System.Collections.Generic.HashSet<MobileDeviceInfo> but if i use following code that uses also a HashSet<> it again works as expected:
bool contains = new HashSet<MobileDeviceInfo>(user.DeviceInfos).Contains(device); // true
So why are only references compared and my custom Equals is ignored?
Update:
even more confusing is the result is false even if i cast it to the
HashSet<MobileDeviceInfo>:
// still false
bool contains2 = ((HashSet<MobileDeviceInfo>)user.DeviceInfos).Contains(device);
// but this is true as already mentioned
bool contains3 = new HashSet<MobileDeviceInfo>(user.DeviceInfos).Contains(device);
Update 2:: the reason for this really seems to be that both HashSets use different comparers. The entity-framework-HashSet uses a:
System.Data.Entity.Infrastructure.ObjectReferenceEqualityComparer
and the standard HashSet<> uses a:
GenericEqualityComparer<T>
That explains the issue, although i don't understand why entity framework uses an implementation that ignores custom Equals implementations under certain circumstances. That's a nasty trap, isn't it?
Conclusion: never use Contains if you don't know what comparer will be used or use Enumerable.Contains with the overload that takes a custom comparer:
bool contains = user.DeviceInfos.Contains(device, EqualityComparer<MobileDeviceInfo>.Default); // true
From the EF source, you might stumble on CreateCollectionCreateDelegate, which seems to be called as part of hooking up navigation properties.
This calls EntityUtil.DetermineCollectionType and returns a HashSet<T> as the type if that is compatible with the property.
Then, armed with HashSet<T>, it makes a call to DelegateFactory.GetNewExpressionForCollectionType which, per the code and the description, handles HashSet<T> as a special case and passes it an ObjectReferenceEqualityComparer in the constructor.
So: the HashSet<T> EF creates for you isn't using your equality implementation, it uses reference equality instead.
Why ICollection<>.Contains ignores my overridden Equals and the IEquatable<> interface?
Because there is no requirement from the implementors of the interface to do so.
ICollection<T>.Contains method MSDN documentation states:
Determines whether the ICollection<T> contains a specific value.
And then
Remarks
Implementations can vary in how they determine equality of objects; for example, List<T> uses Comparer<T>.Default, whereas Dictionary<TKey, TValue> allows the user to specify the IComparer<T> implementation to use for comparing keys.
Side note: Looks like they messed up IComparer<T> with IEqualityComparer<T>, but you get the point :)
Conclusion: never use Contains if you don't know what comparer will be used or use Enumerable.Contains with the overload that takes a custom comparer
According to the Enumerable.Contains<T>(IEnumerable<T>, T) method overload (i.e. without custom comparer) documentation:
Determines whether a sequence contains a specified element by using the default equality comparer.
which sounds like your overrides will be called. But then comes the following:
Remarks
If the type of source implements ICollection<T>, the Contains method in that implementation is invoked to obtain the result. Otherwise, this method determines whether source contains the specified element.
which conflicts with the initial statement.
It's really a mess. All I can say is that I fully agree with that conclusion!

Setting up a simple iequatable class c#

Cant find a simple answer. My problem is I am trying to compare the VALUE of an object in a list to the VALUE of an object...
my class:
public class MatchList
{
public int SomeInt { get; set; }
public decimal SomeDecimal { get; set; }
}
I create theMatchList. It seems that I can only compare the object and not the values for object with 'theMatchList.Contains...'
MatchList ML = new MatchList();
ML.SomeInt = 12;
ML.SomeDecimal = 2.3;
if (theMatchlist.Contains(ML))
{
DoSomething;
}
How do get to fire 'DoSomething'? Assuming that there is an entry in 'theMatchList' where the values equal 12 and 2.3 respectively. I know it has something to do with iequatable, but I dont quite understand how that works. Thanks in advance!
Your naming is a bit unclear, I assume that you actually have a List<MatchList> that you want to find a particular MatchList in (I suggest renaming MatchList to at least MatchItem in that case and preferable something more descriptive).
Then from the documentation of List<T>.Contains:
This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable<T>.Equals method for T (the type of values in the list).
So you will have to implement IEquatable<T> for your class. In addition, the advice is that
[i]f you implement Equals, you should also override the base class implementations of Object.Equals(Object) and GetHashCode so that their behavior is consistent with that of the IEquatable.Equals method.
If you implement GetHashCode, its result should not change over the lifetime of your object. In most cases, making the class immutable is sufficient. If you need to be able to update the fields, you need to implement GetHashCode differently.
So all in all, if you want to use Contains your class will end up looking something like below:
public class MatchList : IEquatable<MatchList>
{
// Note: Fields are readonly to satisfy GetHashCode contract
private readonly int someInt;
private readonly decimal someDecimal;
// Public constructor creates immutable object
public MatchList(int myInt, decimal myDecimal)
{
this.someInt = myInt;
this.myDecimal = myDecimal;
}
// Properties are now read-only too.
public int SomeInt { get { return this.someInt; } }
public decimal SomeDecimal { get { return this.someDecimal; } }
// Implementation of IEquatable<MatchList>
public bool Equals( MatchList other )
{
return (other != null)
&& (this.SomeInt == other.SomeInt)
&& (this.SomeDecimal == other.SomeDecimal);
}
// Override of Object.Equals
// Calls the IEquatable.Equals version if possible.
public override bool Equals( object obj )
{
return (obj is MatchList) && this.Equals(obj as MatchList);
}
public override int GetHashCode()
{
return (this.someInt * 17) ^ this.someDecimal.GetHashCode();
}
}
As I commented, your question is pretty unclear so I'll do my best to explain the concept.
It's pretty likely what you were trying to code is the items in the list not the list itself:
public class MatchItem : IEquatable<MatchItem>
{
public int SomeInt { get; set; }
public decimal SomeDecimal {get; set; }
public bool Equals(MatchItem item)
{
if(item == null)
return false;
return this.SomeInt == item.SomeInt && this.SomeDecimal == item.SomeDecimal;
}
// You should also override object.ToString, object.Equals & object.GetHashCode.
// Omitted for brevity here!
}
You'll note that has an implementation of IEquatable<MatchItem> which allows it to be compared to other instances of MatchItem.
Thereafter, this code will work:
var items = new List<MatchItem>()
{
new MatchItem{SomeInt = 1, SomeDecimal = 0.3M},
new MatchItem{SomeInt = 12, SomeDecimal = 2.3M}
};
var searchItem = new MatchItem{SomeInt = 1, SomeDecimal = 0.3M};
Console.WriteLine(items.Contains(searchItem)); // true
Working example: http://rextester.com/ZWNC6890

How to have type-safe Mongo Object Ids in C#?

Say I have three collections in Mongo: flavor, color, and cupcake. Each collection has its own _id (obviously) and the cupcake collection references the _ids in flavor and cupcake, like so:
{
"_id": ObjectId("123"),
"flavorId": ObjectId("234"),
"colorId": ObjectId("345"),
"moreData": {}
}
This is a toy example, of course, and there is more stuff in these collections. That's not important to this question, except that it's the moreData that I'm really looking for when I query.
I want to be able to look up cupcake objects by flavorId and by colorId (and they are appropriately indexed for such lookups). However, both fields are ObjectId, and I want to avoid somebody accidentally looking for a colorId with a flavorId. How can I design the object and a repository class such that colorId and flavorId will be different types so that the compiler will not allow interchanging them, but still store both ids as ObjectId?
My first thought was to extend ObjectId and pass the extended object around, but ObjectId is a struct which cannot be extended.
You won't be able to prevent those errors, but you can use number intervals to make it easier for "someone" to find the problem.
If I'm not mistaken you can set the ids, so you can use a "prefix" for every kind.
Colors could start with 1000, flavors with 2000 and so on...
Hmm, it is a kind of soft problems, because in most repositories ID is something common (like integers). So having this in mind we could enforce passing an extra parameter instead of changing base object, like this bulletproof solution
cupcakeRepository.Find(ObjectId flavorId, ÒbjectType ÒbjectType.Flavor)
or just extend repository to be more verbose
cupcakeRepository.FindByColor(ObjectId id)
cupcakeRepository.FindByFlavor(ObjectId id)
So I ended up biting the bullet on building the Mongo-specific junk to make a custom class work for this. So here is my drop-in replacement for ObjectId:
public struct DocumentId<T> : IEquatable<DocumentId<T>>
{
static DocumentId()
{
BsonSerializer.RegisterSerializer(typeof(DocumentId<T>), DocumentIdSerializer<T>.Instance);
BsonSerializer.RegisterIdGenerator(typeof(DocumentId<T>), DocumentIdGenerator<T>.Instance);
}
public static readonly DocumentId<T> Empty = new DocumentId<T>(ObjectId.Empty);
public readonly ObjectId Value;
public DocumentId(ObjectId value)
{
Value = value;
}
public static DocumentId<T> GenerateNewId()
{
return new DocumentId<T>(ObjectId.GenerateNewId());
}
public static DocumentId<T> Parse(string value)
{
return new DocumentId<T>(ObjectId.Parse(value));
}
public bool Equals(DocumentId<T> other)
{
return Value.Equals(other.Value);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is DocumentId<T> && Equals((DocumentId<T>)obj);
}
public static bool operator ==(DocumentId<T> left, DocumentId<T> right)
{
return left.Value == right.Value;
}
public static bool operator !=(DocumentId<T> left, DocumentId<T> right)
{
return left.Value != right.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
public override string ToString()
{
return Value.ToString();
}
}
public class DocumentIdSerializer<T> : StructSerializerBase<DocumentId<T>>
{
public static readonly DocumentIdSerializer<T> Instance = new DocumentIdSerializer<T>();
public override DocumentId<T> Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
return new DocumentId<T>(context.Reader.ReadObjectId());
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DocumentId<T> value)
{
context.Writer.WriteObjectId(value.Value);
}
}
public class DocumentIdGenerator<T> : IIdGenerator
{
public static readonly DocumentIdGenerator<T> Instance = new DocumentIdGenerator<T>();
public object GenerateId(object container, object document)
{
return DocumentId<T>.GenerateNewId();
}
public bool IsEmpty(object id)
{
var docId = id as DocumentId<T>? ?? DocumentId<T>.Empty;
return docId.Equals(DocumentId<T>.Empty);
}
}
The type parameter T can be anything; it is never used. It should be the type of your object, like so:
public class Cupcake {
[BsonId]
public DocumentId<Cupcake> Id { get; set; }
// ...
}
This way, your Flavor class has an Id of type DocumentId<Flavor> and your Color class has an Id of type DocumentId<Color>, and never shall the two be interchanged. Now I can create a CupcakeRepository with the following unambiguous methods as well:
public interface ICupcakeRepository {
IEnumerable<Cupcake> Find(DocumentId<Flavor> flavorId);
IEnumerable<Cupcake> Find(DocumentId<Color> colorId);
}
This should be safe with existing data as well because the serialized representation is exactly the same, just an ObjectId("1234567890abcef123456789").

Removing duplicates from a list<int,int> [duplicate]

I have to distinct list of object but NOT only by ID because sometimes two different objects have same ID.
I have class:
public class MessageDTO
{
public MessageDTO(MessageDTO a)
{
this.MsgID = a.MsgID;
this.Subject = a.Subject;
this.MessageText = a.MessageText;
this.ViewedDate = a.ViewedDate;
this.CreatedDate = a.CreatedDate;
}
public int? MsgID { get; set; }
public string Subject { get; set; }
public string MessageText { get; set; }
public System.DateTime? ViewedDate { get; set; }
public System.DateTime? CreatedDate { get; set; }
}
How I can distinct list of:
List<MessageDTO> example;
Thanks
Use LINQ.
public class MessageDTOEqualityComparer : EqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO a, MessageDTO b)
{
// your logic, which checks each messages properties for whatever
// grounds you need to deem them "equal." In your case, it sounds like
// this will just be a matter of iterating through each property with an
// if-not-equal-return-false block, then returning true at the end
}
public int GetHashCode(MessageDTO message)
{
// your logic, I'd probably just return the message ID if you can,
// assuming that doesn't overlap too much and that it does
// have to be equal on the two
}
}
Then
return nonDistinct.Distinct(new MessageDTOEqualityComparer());
You can also avoid the need for an extra class by overriding object.Equals(object) and object.GetHashCode() and calling the empty overload of nonDistinct.Distinct(). Make sure you recognize the implications of this decision, though: for instance, those will then become the equality-testing functions in all non-explicit scopes of their use. This might be perfect and exactly what you need, or it could lead to some unexpected consequences. Just make sure you know what you're getting into.
I you want to use other properties, you should implement IEqualityComparer interface. More on: msdn
class MsgComparer : IEqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO x, MessageDTO Oy)
{
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(MessageDTO m)
{
//it must br overwritten also
}
}
Then:
example.Distinct(new MsgComparer());
You could also overwrite Equals in MessageDTO class:
class MessageDTO
{
// rest of members
public override bool Equals(object obj)
{
// your stuff. See: http://msdn.microsoft.com/en-us/library/ms173147%28v=vs.80%29.aspx
}
public override int GetHashCode()
{
}
}
Then it's enough:
example.Distinct();
You could use the extension method DistinctBy from the MoreLinq library:
string[] source = { "first", "second", "third", "fourth", "fifth" };
var distinct = source.DistinctBy(word => word.Length);
See here:
I recommend you using solution of #Matthew Haugen
In case you don't want to create a new class for that, there is a way to use LINQ by grouping you list by distinct field(s) then select the first item on this group. For example:
example.(e => new { e.MsgID, e.Subject }).Select(grp => grp.FirstOrDefault());

JsonConvert.SerializeObject passes custom type properties to parent types Equals(object) method

I'm seeing some weird behavior serializing with Json.NET v6.0.5 for objects that override the Equals method and have reference type properties, aside from string.
public class TestObject
{
public ChildObject CustomTypeProperty
{
get;
set;
}
public List<string> ListProperty
{
get;
set;
}
public List<ChildObject> ListCustomProperty
{
get;
set;
}
public string StringProperty
{
get;
set;
}
public int IntProperty
{
get;
set;
}
public override bool Equals(object obj)
{
Console.WriteLine(obj.GetType().FullName);
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
public class ChildObject
{
}
Then I serialize it.
var testObj = new TestObject() { CustomTypeProperty = new ChildObject(), IntProperty = 1, ListCustomProperty = new List<ChildObject>(), ListProperty = new List<string>(), StringProperty = "abc" };
var json = JsonConvert.SerializeObject(testObj);
I can see that it calls TestObject.Equals(object obj) three times and passes in not the TestObject, but the CustomTypePropety object, then the ListProperty and then the ListCustomProperty. In my case this causes an InvalidCastException because TestObject tries to cast the obj parameter to a TestObject. I cannot change this in the real-world scenario because the the type is in a third party library.
Is this a bug in Json.NET or am I doing something wrong? I've been digging for a while and cannot find any resolution. Thanks for the help.
EDIT
I just upgraded to Json.NET 6.0.6 and saw the same behavior.
If you are implementing an override for bool Equals(object obj) then you need to handle any type that might be passed to you. You cannot make the assumption that callers will always pass the type you are expecting. The usual solution is to do a simple type check before you cast, like this:
public override bool Equals(object obj)
{
if (obj is TypeYouAreExpecting)
{
TypeYouAreExpecting other = (TypeYouAreExpecting) obj;
bool areEqual = false;
// implement your equals logic here
return areEqual;
}
return base.Equals(obj);
}
If it is a third party library that throws an InvalidCastException in the Equals method, that is definitely a bug. I would contact the author and request that they fix it.
As for why Json.Net calls Equals during serialization on objects of different types, this is done to check for reference loops. See Why does Json.Net call the Equals method on my objects when serializing? for more details.

Categories

Resources