为了账号安全,请及时绑定邮箱和手机立即绑定

如何使用Json.Net对具有附加属性的自定义集合进行序列化/反序列化

/ 猿问

如何使用Json.Net对具有附加属性的自定义集合进行序列化/反序列化

LEATH 2019-12-05 15:59:06

我有一个自定义集合(实现IList),它具有一些自定义属性,如下所示:


class FooCollection : IList<Foo> {


    private List<Foo> _foos = new List<Foo>();

    public string Bar { get; set; }        


    //Implement IList, ICollection and IEnumerable members...


}

序列化时,使用以下代码:


JsonSerializerSettings jss = new JsonSerializerSettings() {

    TypeNameHandling = TypeNameHandling.Auto

};

string serializedCollection = JsonConvert.SerializeObject( value , jss );

它正确地序列化和反序列化所有收集项;但是,FooCollection不会考虑该类中的任何其他属性。


无论如何,有没有将它们包括在序列化中?


查看完整描述

3 回答

?
侃侃尔雅

问题如下:当对象实现时IEnumerable,JSON.net将其标识为值数组,并按照数组Json语法(不包括属性)对其进行序列化,例如:


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

如果要序列化它保留属性,则需要通过定义一个custom来手工处理该对象的序列化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());


查看完整回答
反对 回复 2019-12-05
?
MMTTMM

我个人希望避免JsonConverter在可能的地方编写custom ,而是使用为此目的而设计的各种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以添加这些属性的定义时才有效,否则,您必须采用自定义转换器的方式。


查看完整回答
反对 回复 2019-12-05
?
杨魅力

如果您还想保留列表或集合本身的内容,则可以考虑暴露属性以返回列表。必须进行包装以防止序列化时出现循环问题:


[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()


查看完整回答
反对 回复 2019-12-05

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信