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

C# JSON.NET序列化-DefaultReferenceResolver如何比较相等性?,c#,asp.net-web-api,json.net,C#,Asp.net Web Api,Json.net,我使用的是JSON.NET6.0.3。我已将preserverences选项更改如下: HttpConfiguration.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects; 我的对象图类似于以下内容: public class CarFromManufacturer { public int CarID { get; s

我使用的是JSON.NET6.0.3。我已将
preserverences
选项更改如下:

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; }
}
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();
        }
    }
我的WebAPI控制器正在返回
IEnumerable[CarFromManufacturer]
的结果集。因此,结果可能是来自两个独特制造商对象的5辆汽车的列表。我希望JSON结果只列出一次完全序列化的每个制造商,然后同一制造商的后续使用是$ref ID到原始的$ID。这是不可能的

尽管我找不到一篇文档来说明如何为referencesolver建立平等性,但我已经实现了
IEquatable
,以及
base.Equals
base.GetHashCode()
的覆盖,但运气不好

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

我能想到的唯一一件事是,我正在使用factory对象,而不是先创建每个唯一的
CarManufacturer
,然后创建
CarFromManufacturer
的实例,传入
CarManufacturer
。。。我正在创建
CarManufacturer
的新实例。这可以解释为什么对象不相等,但这就是为什么我实现了
IEquatable
和重写
base.Equals(object)
base.GetHashCode()


我已经研究了,它使用了,它使用了
EqualityComparer
。默认值,从中,使用T的
IEquatable
实现(如果存在),或者使用T的
base.Equals()
实现。。。。所有这些都让我相信
CarManufacturer
中的
IEquatable
应该可以解决我的问题。但是,在
CarManufacturer.Equals()
GethashCode()
中放置断点时,默认情况下JSON.NET解析引用的逻辑只会使用

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

下面是一个采用
IEqualityComparer
来适应您的用例的示例:

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;
    }
}
公共类referencesolver:IReferenceResolver
{
私有字典字符串引用;
私用词典引用;
私人国际参考计数;
公共引用解算器(IEqualityComparer比较器)
{
this.stringToReference=新字典();
this.referenceToString=新字典(比较器);
this.referenceCount=0;
}
公共无效添加引用(
对象上下文,
字符串引用,
对象值)
{
这个.referenceToString.Add((T)值,reference);
此.stringToReference.Add(参考,(T)值);
}
公共字符串GetReference(
对象上下文,
对象值)
{
字符串结果=null;
如果(!this.referenceToString.TryGetValue((T)值,输出结果))
{
referenceCount++;
结果=referenceCount.ToString(CultureInfo.InvariantCulture);
this.referenceToString.Add((T)值,结果);
this.stringToReference.Add(结果,(T)值);
}
返回结果;
}
公共布尔是参考的(
对象上下文,
对象值)
{
返回此.referenceToString.ContainsKey((T)值);
}
公共对象解析引用(
对象上下文,
字符串引用)
{
tr=默认值(T);
this.stringToReference.TryGetValue(参考,out r);
返回r;
}
}

Json.Net将对被比较的对象调用Equals方法。在某些情况下,您可能不希望这样做,但是,例如,当它检查循环引用时,它也会这样做,而检查引用是否相等可能更理想。然而,他们这样做是为了通过重写类中的Equals方法来给予开发人员完全的控制权

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

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

我每次创建Manufacturer的新实例的原因是因为我的factory对象接受联接表记录的EF实体(联接car和Manufacturer)。。因此,它从EF car对象构建汽车,并调用带有EF Manufacturer的ManufacturerFactory。看起来对象引用进行了相等性比较。实现
IReferenceResolver
有什么问题吗?谢谢你的想法!重写Equals和GetHashCode是否不足以让JSON.NET的DefaultReferenceSolver知道如何比较对象?实现IReferenceResolver没有什么错,只是如果没有这个问题,我不需要额外的复杂性。。在这样做之前,我宁愿重新构造工厂代码。但问题是DefaultReferenceSolver如何比较平等。我正在更新帖子。通过查看源代码,我相信它默认使用,它只比较有关更新的引用:
defaultreferencesolver
calls。这个方法使用了我上面提到的比较器,并且没有使用
BidirectionalDictionary
的默认构造函数。非常感谢!理想情况下,可以在HttpConfiguration.Formatters.JsonFormatter.SerializerSettings.ReferenceResolver全局应用解析器,并使用依赖项解析。我将大胆地假设所有将被序列化的模型对象都是不可变的,因此对于序列化而言,引用平等性并不重要。谢谢如果你有任何见解。。。这里也讨论了这一点: