问题
I just ran into an error when I was using the Newtonsoft.Json SerializeObject
method. It has been asked before here, but there was no answer from the people working with Newtonsoft as to why this happens.
Basically, when calling SerializeObject
like this:
string json = Newtonsoft.Json.JsonConvert.SerializeObject(from, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
I get errors in a lot of Equals
methods I have overridden in my classes:
public override bool Equals(object obj)
{
if (obj == null)
return false;
CapacityConfiguration cc = (CapacityConfiguration)obj; // <-- TypeCastException here; other Properties of the same class are sent in as parameter!
}
And of course I realize that it's "easy" to fix, by checking like this:
public override bool Equals(object obj)
{
if (obj is CapacityConfiguration == false)
return false;
CapacityConfiguration cc = (CapacityConfiguration)obj;
}
But the real question is:
Why does Json.Net send in other types of objects in the Equals
method of the class? More specifically, Json.Net seems to send in a lot of other properties in the class, instead of another object of the same type.
To me, it's completely weird. Any input would be appreciated.
I am using "Version 8.0.0.0" according to Visual Studio.
UPDATE 1
It's easy to test, as it is reproducible:
public class JsonTestClass
{
public string Name { get; set; }
public List<int> MyIntList { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
JsonTestClass jtc = (JsonTestClass)obj;
return true;
}
}
And then just place this code in Program.cs or anywhere else:
JsonTestClass c = new JsonTestClass();
c.Name = "test";
c.MyIntList = new List<int>();
c.MyIntList.Add(1);
string json = Newtonsoft.Json.JsonConvert.SerializeObject(c, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
and you will get the TypeCast Exception:
回答1:
Why will JsonConvert.SerializeObject call the
object.Equals
method?
Because when you use JsonConvert.SerializeObject
, there is a method CheckForCircularReference which is called to check whether a property re-references your own object, leading to an infinite loop.
private bool CheckForCircularReference(JsonWriter writer, object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty)
In the CheckForCircularReference
method, part of code uses the Contains
method, which will call object.Equals
if your object
didn't implement the IEquatable<T>
interface.
bool exists = (Serializer._equalityComparer != null)
? _serializeStack.Contains(value, Serializer._equalityComparer)
: _serializeStack.Contains(value);
Explanation
_serializeStack
is a list of objects currently being serialized.- The
List<T>.Contains
method checks whether the current property is or isn't contained in the collection. List<T>.Contains
usesEqualityComparer<T>.Default
, which in turn usesIEquatable<T>
if the type implements it, orobject.Equals
otherwise.- The
object value
parameter is your currentProperty
object.
Here is an example of a self-referencing loop:
public class JsonTestClass
{
public string Name { get; set; }
public List<int> MyIntList { get; set; }
public JsonTestClass Test{get;set;}
public override bool Equals(object obj)
{
if (obj == null)
return false;
JsonTestClass jtc = (JsonTestClass)obj;
return true;
}
}
JsonTestClass c = new JsonTestClass();
c.Name = "test";
c.Test = c;
string json = JsonConvert.SerializeObject
(c, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
We will get an exception:
Self referencing loop detected for property 'test' with type 'Program+JsonTestClass'. Path ''.
But if we do it like this there is no error:
JsonTestClass c = new JsonTestClass();
c.Name = "test";
c.Test = new JsonTestClass();
string json = JsonConvert.SerializeObject
(c, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
来源:https://stackoverflow.com/questions/51669072/why-does-json-net-call-the-equals-method-on-my-objects-when-serializing