Сериализация / десериализация пользовательской коллекции с дополнительными свойствами с помощью Json.Net

У меня есть пользовательская коллекция (реализующая IList), которая имеет некоторые пользовательские свойства, как показано ниже:

class FooCollection : IList { private List _foos = new List(); public string Bar { get; set; } //Implement IList, ICollection and IEnumerable members... } 

Когда я сериализую, я использую следующий код:

 JsonSerializerSettings jss = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto }; string serializedCollection = JsonConvert.SerializeObject( value , jss ); 

Он сериализует и десериализует все элементы коллекции должным образом; однако любые дополнительные свойства в classе FooCollection не учитываются.

Нужно ли включать их в сериализацию?

Проблема заключается в следующем: когда объект реализует IEnumerable , JSON.net идентифицирует его как массив значений и сериализует его после синтаксиса Json массива (который не включает свойства), например:

  [ {"FooProperty" : 123}, {"FooProperty" : 456}, {"FooProperty" : 789}] 

Если вы хотите сериализовать его, сохраняя свойства, вам необходимо обработать сериализацию этого объекта вручную, JsonConverter пользовательский JsonConverter :

 // intermediate class that can be serialized by JSON.net // and contains the same data as FooCollection class FooCollectionSurrogate { // the collection of foo elements public List Collection { get; set; } // the properties of FooCollection to serialize public string Bar { get; set; } } public class FooCollectionConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(FooCollection); } public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // NB null handling is missing var surrogate = serializer.Deserialize(reader); var fooElements = surrogate.Collection; var fooColl = new FooCollection { Bar = surrogate.Bar }; foreach (var el in fooElements) fooColl.Add(el); return fooColl; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // NB null handling is missing var fooColl = (FooCollection)value; // create the surrogate and serialize it instead // of the collection itself var surrogate = new FooCollectionSurrogate() { Collection = fooColl.ToList(), Bar = fooColl.Bar }; serializer.Serialize(writer, surrogate); } } 

Затем используйте его следующим образом:

 var ss = JsonConvert.SerializeObject(collection, new FooCollectionConverter()); var obj = JsonConvert.DeserializeObject(ss, new FooCollectionConverter()); 

Лично мне нравится избегать написания пользовательского JsonConverter где это возможно, и вместо этого использовать различные атрибуты JSON, которые были разработаны для этой цели. Вы можете просто украсить FooCollection с помощью JsonObjectAttribute , который заставляет сериализацию как объект JSON, а не массив. Вам нужно будет украсить свойства Count и IsReadOnly с помощью JsonIgnore чтобы они не отображались на выходе. Если вы хотите сохранить _foos в частном поле, вам также придется украсить его JsonProperty .

 [JsonObject] class FooCollection : IList { [JsonProperty] private List _foos = new List(); public string Bar { get; set; } // IList implementation [JsonIgnore] public int Count { ... } [JsonIgnore] public bool IsReadOnly { ... } } 

Сериализация дает что-то вроде следующего:

 { "_foos": [ "foo1", "foo2" ], "Bar": "bar" } 

Очевидно, что это работает только в том случае, если вы можете изменить определение FooCollection , чтобы добавить эти атрибуты, в противном случае вам придется идти по пути пользовательских преобразователей.

Работает ли наследство от Списка?

 class FooCollection : List, IList { public string Bar { get; set; } //Implement IList, ICollection and IEnumerable members... } 

Если вы также хотите сохранить содержимое самого списка или коллекции, вы можете рассмотреть, что выставлять свойство возвращает список. Он должен быть обертыванием для предотвращения циклической проблемы при сериализации:

 [JsonObject] public class FooCollection : List { [DataMember] public string Bar { get; set; } = "Bar"; public ICollection Items => new _(this); } public class _ : ICollection { public _(ICollection collection) => Inner = collection; public ICollection Inner { get; } public int Count => this.Inner.Count; public bool IsReadOnly => this.Inner.IsReadOnly; public void Add(T item) => this.Inner.Add(item); public void Clear() => this.Inner.Clear(); public bool Contains(T item) => this.Inner.Contains(item); public void CopyTo(T[] array, int arrayIndex) => this.Inner.CopyTo(array, arrayIndex); public IEnumerator GetEnumerator()=> this.Inner.GetEnumerator(); public bool Remove(T item) => this.Inner.Remove(item); IEnumerator IEnumerable.GetEnumerator() => this.Inner.GetEnumerator(); } 

new FooCollection { 1, 2, 3, 4, 4 } =>

 { "Bar": "Bar", "Items": [ 1, 2, 3 ], "Capacity": 4, "Count": 3 } 

new FooCollection { 1, 2, 3 }.ToArray() => new []{1, 2, 3}.ToArray()

  • Есть ли у .NET 4 встроенный сериализатор / десериализатор JSON?
  • Как получить json.net для сериализации членов classа, полученных из List ?
  • Каковы различия между XmlSerializer и BinaryFormatter
  • Использование jQuery для захвата содержимого из iframe CKEditor
  • Специальная сериализация Jackson JSON для определенных полей
  • Рекомендации по сериализации объектов в пользовательский строковый формат для использования в выходном файле
  • Пользовательский серализатор Gson для одной переменной (из многих) в объекте с использованием TypeAdapter
  • Json.net сериализует определенное частное поле
  • Преобразование записей в последовательную форму данных для отправки по HTTP
  • Json.Net: свойство Serialize / Deserialize в качестве значения, а не как объект
  • JSON.NET Parser * кажется * будет двойной сериализацией моих объектов
  • Давайте будем гением компьютера.