【问题标题】:JSON.NET Serialization - How does DefaultReferenceResolver compare equality?JSON.NET 序列化 - DefaultReferenceResolver 如何比较相等性?
【发布时间】:2014-08-29 11:56:18
【问题描述】:

我正在使用 JSON.NET 6.0.3。我已将PreserveReferences 选项更改如下:

HttpConfiguration.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;

我的对象图如下所示:

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

我的 WebAPI 控制器正在返回 IEnumerable[CarFromManufacturer] 的结果集。因此,结果可能是来自两个唯一制造商对象的 5 辆汽车的列表。我期望 JSON 结果仅在完全序列化后列出每个制造商,然后将同一制造商的后续使用作为原始 $id 的 $ref ID。这不会发生。

尽管我找不到一篇说明如何为 ReferenceResolver 建立相等性的文档,但我已经实现了 IEquatable<CarManufacturer> 以及 base.Equalsbase.GetHashCode() 的覆盖,但没有运气。

我想避免实现我自己的IReferenceResolver,因为在同一个项目中有非常相似的对象图按预期工作。

我唯一能想到的是我正在使用工厂对象,而不是先创建每个唯一的 CarManufacturer,然后创建 CarFromManufacturer 的实例,传入 CarManufacturer... 我正在创建一个新实例的CarManufacturer。这可以解释为什么对象不相等,但这就是我实现IEquatable 并覆盖base.Equals(object)base.GetHashCode() 的原因。

我查看了source for DefaultReferenceResolver,它使用default constructor of BidirectionalDictionary,它使用EqualityComparer<T>。默认情况下,来自MSDN documentation,使用T的实现IEquatable<T>(如果存在),否则使用T的@ 987654343@ 实现....所有这些都会让我相信CarManufacturer 中的IEquatable 应该可以解决我的问题。但是,在CarManufacturer.Equals()GethashCode() 中放置断点从未命中..

【问题讨论】:

  • 我每次都创建制造商的新实例的原因是因为我的工厂对象接受了连接表记录的 EF 实体(连接汽车和制造商).. 所以它从EF Car 对象并使用 EF 制造商调用 ManufacturerFactory。
  • 看起来对象引用被比较是否相等。实现IReferenceResolver 有什么问题?
  • 感谢您的考虑!重写 Equals 和 GetHashCode 是否不足以让 JSON.NET 的 DefaultReferenceResolver 知道如何比较对象?实现 IReferenceResolver 没有任何问题,只是如果不是因为这个问题,我就不需要额外的复杂性。我宁愿在这样做之前重组工厂代码。但问题实际上是关于 DefaultReferenceResolver 如何比较相等性。我正在更新帖子..
  • 查看源码,相信默认使用this comparer,只比较引用
  • 关于您的更新DefaultReferenceResolver 致电JsonSerializerInternalBase.DefaultReferenceMappings。这个方法使用了我上面提到的比较器,没有使用BidirectionalDictionary的默认构造函数。

标签: c# asp.net-web-api json.net


【解决方案1】:

JSON.NET 默认解析引用的逻辑只是使用this comparer 比较引用。

如果您想以不同的方式比较对象,则必须实现自定义 IReferenceResolver

这是一个使用IEqualityComparer<T> 来适应您的用例的示例:

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

【讨论】:

  • 非常感谢!理想情况下,解析器可以在 HttpConfiguration.Formatters.JsonFormatter.SerializerSettings.ReferenceResolver 全局应用,并利用依赖解析。我将大胆假设所有将被序列化的模型对象都是不可变的,因此 ReferenceEquality 并不重要——出于序列化的目的。谢谢!
【解决方案2】:

Json.Net 将对正在比较的对象调用 Equals 方法。在某些情况下,您可能不希望这样做,例如,当它检查循环引用时,它会执行相同的操作,而检查引用相等性可能更理想。然而,他们这样做是为了通过覆盖类中的 Equals 方法来让开发人员完全控制。

您可以覆盖默认实现。例如,要使其成为引用相等,您可以执行以下操作:

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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2012-12-26
  • 1970-01-01
  • 2021-04-19
  • 2016-11-05
  • 1970-01-01
  • 2016-03-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多