JSON.NET序列化 - DefaultReferenceResolver如何比较相等?

时间:2014-08-29 11:56:19

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

我正在使用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结果只列出每个制造商一次完全序列化,然后将同一制造商的后续使用作为$ ref ID到原始的$ id。那种情况没有发生。

即使我找不到单一文档来说明如何为ReferenceResolver建立相等性,我已经实现了IEquatable以及base.Equals和base.GetHashCode()的覆盖,但没有运气。

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

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

我查看了source for DefaultReferenceResolver并使用了使用EqualityComparer.Default的default constructor of BidirectionalDictionary,它来自MSDN documentation,使用了Tqu的IEquatable实现它存在,或以其他方式使用T的base.Equals()实现......所有这些都会让我相信CarManufacturer中的IEquatable应该解决我的问题。但是,在CarManufacturer.Equals()和GethashCode()中放置断点永远不会命中..

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:1)

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