如何使用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 ); 

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

无论如何将它们包含在序列化中?

问题如下:当一个对象实现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 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属性。 您可以使用JsonObjectAttribute简单地修饰FooCollection ,这会强制序列化为JSON对象而不是数组。 您必须使用JsonIgnore修饰CountIsReadOnly属性,以防止它们出现在输出中。 如果你想将_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的定义以添加这些属性时, FooCollection ,否则您必须采用自定义转换器的方式。

inheritanceList工作吗?

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

如果您还想保留List或集合本身的内容,可以考虑公开属性返回列表。 它必须是包装以防止在序列化时出现循环问题:

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