序列化集合类时双包含对象
本文关键字:包含 对象 集合类 序列化 | 更新日期: 2023-09-27 18:16:13
我正在尝试序列化类Poll,它看起来像这样:
class Poll
{
(...) //methods
public AnswersCollection answers { get; set; }
public TagsCollection tags { get; set; }
public string question { get; set; }
}
如您所见,我有"TagsCollection"answers"AnswersCollection",它们看起来非常相似,所以我只展示其中一个。
class AnswersCollection
{
(...) //methods
public List<Answer> answers { get; set; }
}
最后,回答课堂。
class Answer
{
(...) //methods
public string name { get; set; }
public uint voteQuantity { get; set; }
}
所有的类都有默认的公共构造函数(没有参数),所以JSON。. NET没有任何序列化问题。
问题是与AnswersCollection(这是封装),因为它,JSON输出看起来像:
{
"answerswers":{
"answerswers":[
{
"name":"Foo",
"voteQuantity":45
},
{
"name":"Bar",
"voteQuantity":30
}
]
},
"tags":{
"tags":[
{
"name":"FooTag",
"id":5
},
{
"name":"BarTag",
"id":4
}
]
},
"question":"Question?"
}
正如你所看到的,问题在于像"answerswers":{"answerswers":[(…)]}这样的结构
是否有选项序列化它的结构,如"答案":[(…)]没有第二个"答案"标签?我尝试使用属性,如"isReference",但它没有工作。
序列化实际上正在做期望的事情。
我知道它不会完全回答你原来的问题,是否有一种方法来问Json。Net可以做你想做的事情,但是你最好的选择是为你的AnswersCollection
使用继承而不是组合。
既然这个名字暗示这个类是一个答案的集合,为什么不让它继承自List<Answer>
而不是拥有这个类型的属性呢?
如果你真的不能改变对象的结构,你可以走一条漫长而艰难的路,实现你的自己的 JsonConverter来绝对控制你的属性将如何被序列化/反序列化。
你必须应用JsonConverterAttribute到Poll.answers
属性来告诉Json。. Net使用自定义序列化器。
但是,如果可以的话,我强烈建议不要采用这种方法。
离题:你应该考虑使用CamelCasePropertyNamesContractResolver来告诉你的序列化器在序列化你的属性时使用CamelCasePropertyNamesContractResolver,这样你就不必在你的属性本身上使用CamelCasePropertyNamesContractResolver:如果你想遵循常见的命名实践,answers
应该拼写为Answers
。
如果您不需要来回序列化(换句话说,您只需要序列化而不需要反序列化),那么获得您想要的结果的一种简单方法是使您的集合类实现IEnumerable<T>
,如下所示:
class AnswersCollection : IEnumerable<Answer>
{
public List<Answer> answers { get; set; }
public IEnumerator<Answer> GetEnumerator()
{
return answers != null ? answers.GetEnumerator() : new List<Answer>().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
这个工作是因为Json。Net在序列化过程中自动将实现IEnumerable
的类视为数组。
如果您也需要反序列化,那么您需要更进一步,执行以下操作之一:
- 添加一个接受
IEnumerable<T>
的构造函数,正如下面注释中的@dbc所建议的那样; - 让你的类实现
ICollection<T>
,或者 - 就像@Fabio Salvalai在他的回答中建议的那样,让你的Collection类继承
List<T>
。
另一个选择,如果你不想改变你的类,是实现一个自定义JsonConverter
来处理自定义序列化/反序列化。转换器可能看起来像这样:
class CollectionConverter<TCollection, TItem> : JsonConverter where TCollection : new()
{
private string ListPropertyName { get; set; }
public CollectionConverter(string listPropertyName)
{
ListPropertyName = listPropertyName;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(TCollection);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
List<TItem> list = (List<TItem>)typeof(TCollection).GetProperty(ListPropertyName).GetValue(value, null);
JArray array = JArray.FromObject(list);
array.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
List<TItem> list = array.ToObject<List<TItem>>();
TCollection collection = new TCollection();
typeof(TCollection).GetProperty(ListPropertyName).SetValue(collection, list);
return collection;
}
}
要使用转换器,需要向Poll类中的Collection属性添加[JsonConverter]
属性,如下所示:
class Poll
{
[JsonConverter(typeof(CollectionConverter<AnswersCollection, Answer>), "answerswers")]
public AnswersCollection answers { get; set; }
[JsonConverter(typeof(CollectionConverter<TagsCollection, Tag>), "tags")]
public TagsCollection tags { get; set; }
public string question { get; set; }
}
那么就像往常一样序列化和反序列化。