【问题标题】:Why does Json.Net call the Equals method on my objects when serializing?为什么 Json.Net 在序列化时对我的对象调用 Equals 方法?
【发布时间】:2018-08-03 09:06:56
【问题描述】:

我在使用 Newtonsoft.Json SerializeObject 方法时遇到了错误。在here 之前有人问过这个问题,但与 Newtonsoft 合作的人没有回答为什么会发生这种情况。

基本上,当像这样调用SerializeObject 时:

string json = Newtonsoft.Json.JsonConvert.SerializeObject(from, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });

我在我的类中覆盖了很多 Equals 方法中的错误:

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!
}

当然,我意识到通过这样的检查“很容易”修复:

public override bool Equals(object obj)
{
    if (obj is CapacityConfiguration == false)
        return false;

    CapacityConfiguration cc = (CapacityConfiguration)obj;
}

但真正的问题是: 为什么 Json.Net 在类的 Equals 方法中发送其他类型的对象? 更具体地说,Json.Net 似乎在类中发送了很多其他属性,而不是另一个同一类型的对象。

对我来说,这完全是奇怪的。任何意见将不胜感激。

根据 Visual Studio,我使用的是“版本 8.0.0.0”。

更新 1

很容易测试,因为它是可重现的:

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;
    }
}

然后只需将此代码放在 Program.cs 或其他任何地方:

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 });

你会得到 TypeCast 异常:

【问题讨论】:

  • 有机会提供minimal reproducible example吗?
  • 这只是为了检测对象图中可能的循环吗?
  • @LasseVågsætherKarlsen 好吧,我没有准备好,没有。当我在我的大型项目中转换一些数据时,我正在运行它,所以我没有制作一个测试项目。我想也许它可能是众所周知的事情,有人可以解释,但我会整理一个测试,看看我是否可以在那里复制它。
  • 无论如何,可能的原因可能是 json.net 正在查看您是否正在序列化循环对象(直接引用自身的对象)。你的 Equals(object) 方法应该准备好处理任何类型,所以不管 json.net 是什么,这就是这样做的方法。
  • 我已经用上面的“UPDATE 1”更新了我的问题,很容易重现。 @LasseVågsætherKarlsen 这是猜测还是您知道这是事实?因为我最初的想法是有一个严重的错误......?

标签: c# json json.net equals serialization


【解决方案1】:

为什么JsonConvert.SerializeObject会调用object.Equals方法?

因为当你使用JsonConvert.SerializeObject时,有一个方法CheckForCircularReference被调用来检查一个属性是否重新引用了你自己的对象,从而导致一个无限循环。

 private bool CheckForCircularReference(JsonWriter writer, object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty)

CheckForCircularReference方法中,部分代码使用了Contains方法,如果你的object没有实现IEquatable&lt;T&gt;接口,它会调用object.Equals

bool exists = (Serializer._equalityComparer != null)
                ? _serializeStack.Contains(value, Serializer._equalityComparer)
                : _serializeStack.Contains(value);

说明

  1. _serializeStack 是当前正在序列化的对象列表。
  2. List&lt;T&gt;.Contains 方法检查当前属性是否包含在集合中。
  3. List&lt;T&gt;.Contains 使用 EqualityComparer&lt;T&gt;.Default,如果类型实现了它,则反过来使用 IEquatable&lt;T&gt;,否则使用 object.Equals
  4. object value 参数是您当前的Property 对象。

这是一个自引用循环的example

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 });

我们会得到一个异常:

检测到类型为“Program+JsonTestClass”的属性“test”的自引用循环。路径''。

但如果我们这样做,就没有错误:

JsonTestClass c = new JsonTestClass();
c.Name = "test";
c.Test = new JsonTestClass();

string json = JsonConvert.SerializeObject
       (c, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });

【讨论】:

  • 好的,那么这是设计使然。对我来说已经足够了。
猜你喜欢
  • 2019-11-25
  • 1970-01-01
  • 1970-01-01
  • 2013-07-23
  • 1970-01-01
  • 1970-01-01
  • 2013-11-14
  • 2012-06-20
相关资源
最近更新 更多