2014-08-29 1 views
4

Я использую JSON.NET 6.0.3. Я изменил вариант PreserveReferences следующим образом:JSON.NET Сериализация - Как DefaultReferenceResolver сравнивает равенство?

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, потому что у него очень похожие графические объекты, работающие как ожидалось в том же проекте.

Единственное, что я могу придумать, это то, что я использую фабричные объекты и вместо того, чтобы сначала создавать каждый уникальный автопроизводитель, а затем создавать экземпляры CarFromManufacturer, проходящие в CarManufacturer ... Я создаю новый экземпляр CarManufacturer. Это объясняет, почему объекты не равны, но именно поэтому я реализовал IEquatable и переопределяет base.Equals (object) и base.GetHashCode().

Я посмотрел в source for DefaultReferenceResolver и использует default constructor of BidirectionalDictionary, который использует EqualityComparer.Default, который с MSDN documentation, использует реализацию Т по IEquatable, если она существует, или иным образом использует base.Equals T в реализации() .... все это привело бы меня к тому, что IEquatable в CarManufacturer должен решить мою проблему. Однако размещение точек останова в CarManufacturer.Equals() и GethashCode() никогда не удавалось ..

+0

Причина, почему я создаю новый экземпляр производителя каждый раз, потому что мой завод объект принимает в EF субъекта объединения записи таблицы (присоединяется автомобиль и производитель) .. поэтому он строит автомобиль у объекта EF Car и обращается к изготовителю с производителем EF. – diegohb

+0

Похоже, что ссылки на объекты сравниваются для равенства. Что случилось с внедрением «IReferenceResolver»? –

+0

Спасибо за мысль!Недостаточно ли переопределения Equals и GetHashCode, чтобы позволить JSON.NET DefaultReferenceResolver знать, как сравнивать объекты? С реализацией IReferenceResolver ничего не происходит, за исключением того, что мне не нужна эта дополнительная сложность, если бы не эта проблема. Я бы предпочел перестроить заводский код, прежде чем делать это. Но вопрос в том, как DefaultReferenceResolver сравнивает равенство. Я обновляю сообщение. – diegohb

ответ

2

Логика 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; 
    } 
} 
+0

Большое спасибо! В идеале, распознаватель может применяться глобально в HttpConfiguration.Formatters.JsonFormatter.SerializerSettings.ReferenceResolver и использует разрешение зависимости. Я смело предполагаю, что все объекты модели, которые будут сериализованы, неизменны, поэтому ReferenceEquality не имеет значения - для целей сериализации. благодаря! – diegohb

+0

Если у вас есть понимание ... https://github.com/JamesNK/Newtonsoft.Json/issues/411 – diegohb

0

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

Это также обсуждается здесь: https://github.com/JamesNK/Newtonsoft.Json/issues/401 –

Смежные вопросы