Closed MTantos1 closed 5 years ago
Is this the exact output from the serializer?
Can you supply test code?
Is this the exact output from the serializer?
Yes.
Can you supply test code?
Not at the moment, will try put together a small project when I am at a dev machine.
Sorry it has taken me so long to reply.
But I found the source of the problem and can replicate with a simple example below. The error arrises when an object "Equal" to that returned by the default constructor exists in the object graph being serialised / deserialised.
The serialiser correctly identifies the objects as NonEqual on serialisation (as they have all their properties set at this point), however the Deserialiser, when populating _circobj
incorrectly identifies new
objects as "Equal" to one already present in the dictionary, as it does this comparison before setting the properties on the object.
The IEqualityComparer
used for these dictionaries should be a ReferenceEqual equivalent.
See example of ReferenceEqualComparer
at StackOverflow.
public class Circle
{
public Point Center { get; set; }
public int Radius { get; set; }
}
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public Point() { X = Y = 0; }
public Point(int x, int y)
{
X = x;
Y = y;
}
public override bool Equals(object obj)
{
if (obj is Point p) return p.X == X && p.Y == Y;
return false;
}
public override int GetHashCode() => X + Y;
}
public class Tests
{
[Test]
public void Test1()
{
var circles = new Circle[]
{
new Circle() { Center = new Point(0, 0), Radius = 1 },
new Circle() { Center = new Point(0, 1), Radius = 2 },
new Circle() { Center = new Point(0, 1), Radius = 3 }
};
var json = JSON.ToJSON(cirles);
circles = JSON.ToObject<Circle[]>(json);
}
}
I've added the ReferenceEqualComparer
to the object checking and it works however the above code does not output $i
the following code does :
var p = new Point(0, 1);
var circles = new Circle[]
{
new Circle() { Center = new Point(0, 0), Radius = 1 },
new Circle() { Center = p, Radius = 2 },
new Circle() { Center = p, Radius = 3 }
};
which is probably what you would expect.
Weirdly if you remove the Point.Equals()
code everything works without the ReferenceEqualComparer
.
Sorry if you remove the Point.GetHashCode()
code.
Probably not a good idea to mess with GetHashCode()
since from your code it will result in hash conflicts.
Ah, but you must.
It is a requirement for Dictionary to work as intended.
Equal objects must have the same hash code.
i.e. If a.Equals(b)
then a.GetHashCode() == b.GetHashCode()
can be inferred with certainty.
If I didn't override GetHashCode()
then the following would hold:
Point p1 = new Point(0,0);
Point p2 = new Point(0,0);
Console.WriteLine(p1.Equals(p2)); // True
Console.WriteLine(p1.GetHashCode() == p2.GetHashCode()); // False
Which would mean the Dictionary would not behave correctly (when using Points as keys).
I am by no means saying X + Y
is a good hash function, but it meets the criteria, and this was just a simple example to show the problem.
Edit:
See this post on why GetHashCode()
needs to be overridden
fixed in v2.2.6 behind JSONParameters.OverrideObjectHashCodeChecking = true
I have a class I'm trying to serialise and then deserialise using your library and it throws an InvalidCastException when trying to Deserialise the following json
From what I can see the library appears to be reducing output by using an internal dictionary to serialise "Equal" objects as
"$i": n
. Based on this assumption the final Address in the json array has a Suburb (replaced by "$i": 28) which throws an InvalidCastExceptionUnable to cast object of type 'Common.Lib.Address' to type 'Common.Lib.Suburb'.
The interface for Address and Suburb are as follows: