我有一个自定义集合(实现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
类中的任何额外属性都不会被考虑在内。
有没有将它们包含在序列化中?
答案 0 :(得分:23)
问题如下:当一个对象实现IEnumerable
时,JSON.net将其标识为值数组并将其序列化为数组Json syntax(不包括属性),
例如:
[ {"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());
答案 1 :(得分:8)
就个人而言,我希望尽可能避免编写自定义JsonConverter
,而是使用为此目的而设计的各种JSON属性。您可以使用JsonObjectAttribute
简单地装饰FooCollection
,这会强制序列化为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
的定义才能添加这些属性时,这才有效,否则您必须采用自定义转换器的方式。
答案 2 :(得分:2)
如果您不想编写自定义JsonConverter
或使用JSON属性(JsonObjectAttribute),则可以使用以下扩展方法:
public static string ToFooJson<T>(this FooCollection fooCollection)
{
return JsonConvert.SerializeObject(new
{
Bar = fooCollection.Bar,
Collection = fooCollection
});
}
答案 3 :(得分:0)
继承自List工作吗?
class FooCollection : List<Foo>, IList<Foo>
{
public string Bar { get; set; }
//Implement IList, ICollection and IEnumerable members...
}
答案 4 :(得分:0)
如果您还想保留List或集合本身的内容, 您可以考虑公开属性返回列表。它必须包装以防止序列化时出现循环问题:
[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 }
=&gt;
{
"Bar": "Bar",
"Items": [
1,
2,
3
],
"Capacity": 4,
"Count": 3
}
new FooCollection { 1, 2, 3 }.ToArray()
=&gt; new []{1, 2, 3}.ToArray()
答案 5 :(得分:0)
我最近正在测试类似的东西,并提出了以下不需要自定义序列化程序的示例(与 Ahmed Alejo 的答案非常相似,但不需要公共属性):
void Main()
{
var test = new Container();
test.Children.Add("Item 1");
test.Children.Add("Item 2");
test.Children.Add("Item 3");
Console.WriteLine(JsonConvert.SerializeObject(test));
}
class Container {
public CustomList Children { get; set; } = new CustomList() { Name = Guid.NewGuid().ToString() };
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
class CustomList : IList<string>
{
private List<string> items = new List<string>();
private string name;
public string this[int index] { get => items[index]; set => items[index] = value; }
public string Name { get => name; set { name = value; } }
public int Count => items.Count;
public bool IsReadOnly => false;
public void Add(string item)
{
items.Add(item);
}
public void Clear()
{
items.Clear();
}
public bool Contains(string item) => items.Contains(item);
public void CopyTo(string[] array, int arrayIndex)
{
items.CopyTo(array, arrayIndex);
}
public IEnumerator<string> GetEnumerator() => items.GetEnumerator();
public int IndexOf(string item) => items.IndexOf(item);
public void Insert(int index, string item)
{
items.Insert(index, item);
}
public bool Remove(string item) => items.Remove(item);
public void RemoveAt(int index)
{
items.RemoveAt(index);
}
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)items).GetEnumerator();
}
关键是在类中添加显式 JsonObjectAttribute
,它将把它当作一个对象而不是一个数组(也可以使用 JsonArrayAttribute
显式表示)。上述测试的(美化)输出如下:
{
"Children": {
"items": [
"Item 1",
"Item 2",
"Item 3"
],
"name": "ff8768f7-5efb-4622-b7e2-f1472e80991c"
}
}