Сериализовать IEnumerable с Json.NET с другими свойствами [duplicate]

Вы просто поместили библиотеку jQuery в раздел заголовка html:)

<head>
<script src="assets/js/jquery-1.10.2.min.js"></script>
<script src="assets/js/bootstrap.min.js"></script>
</head>
18
задан Brian Rogers 13 April 2016 в 14:50
поделиться

4 ответа

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

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

Если вы хотите сериализовать его, сохраняя свойства, вам необходимо обработать сериализацию этого объекта вручную, указав пользовательский 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<Foo> 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)
    {
        // N.B. null handling is missing
        var surrogate = serializer.Deserialize<FooCollectionSurrogate>(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)
    {
        // N.B. 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<FooCollection>(ss, new FooCollectionConverter());
23
ответ дан digEmAll 25 August 2018 в 13:00
поделиться

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

[JsonObject]
public class FooCollection : List<int>
{
    [DataMember]
    public string Bar { get; set; } = "Bar";
    public ICollection<int> Items => new _<int>(this);
}

public class _<T> : ICollection<T>
{
    public _(ICollection<T> collection) => Inner = collection;    
    public ICollection<T> 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<T> 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()

0
ответ дан Ahmed Alejo 25 August 2018 в 13:00
поделиться

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

[JsonObject]
class FooCollection : IList<Foo> {
    [JsonProperty]
    private List<Foo> _foos = new List<Foo>();
    public string Bar { get; set; }  

    // IList implementation
    [JsonIgnore]
    public int Count { ... }
    [JsonIgnore]
    public bool IsReadOnly { ... }
}

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

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

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

7
ответ дан Jeff E 25 August 2018 в 13:00
поделиться

Находит ли наследование из списка?

class FooCollection : List<Foo>, IList<Foo>
{
    public string Bar { get; set; }        
    //Implement IList, ICollection and IEnumerable members...
}
0
ответ дан qujck 25 August 2018 в 13:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: