序列化集合类时双重包含对象

时间:2016-11-03 14:18:25

标签: c# json serialization json.net

我试图序列化类Poll,看起来像这样:

class Poll
{
    (...) //methods
    public AnswersCollection answers { get; set; }
    public TagsCollection tags { get; set; }
    public string question { get; set; }
}

正如你所看到的,我有" TagsCollection"和" AnswersCollection",两者看起来都很相似,所以我只展示其中一个。

class AnswersCollection
{
    (...) //methods
    public List<Answer> answers { get; set; }
}

而且,最后,答案课。

 class Answer
{
    (...) //methods
    public string name { get; set; }
    public uint voteQuantity { get; set; }
}

所有clases都有默认的公共构造函数(没有参数),因此JSON.NET在序列化方面没有任何问题。

问题在于AnswersCollection(它是封装),因为它,JSON输出看起来像这样:

{
    "answers":{
        "answers":[
            {
                "name":"Foo",
                "voteQuantity":45
            },
            {
                "name":"Bar",
                "voteQuantity":30
            }
        ]
    },
    "tags":{
        "tags":[
            {
                "name":"FooTag",
                "id":5
            },
            {
                "name":"BarTag",
                "id":4
            }
        ]
    },
    "question":"Question?"
}

正如你所看到的,问题在于结构如&#34;答案&#34;:{&#34;答案&#34;:[(...)]}

是否可以选择将其序列化为像&#34;答案&#34;等结构。 :[(...)]没有第二个&#34;答案&#34;标签? 我试图使用&#34; isReference&#34;等属性。但它没有奏效。

2 个答案:

答案 0 :(得分:2)

序列化实际上完全符合预期。

我知道它不会完全回答你原来的问题,是否有办法让Json.Net做你想做的事,但你最好的选择就是为你的{{1}设置继承而不是组合。 }。

由于名称表明该类是答案的集合,为什么不将其从AnswersCollection继承而不是具有此类型的属性?

如果确实无法改变对象的结构,那么您可以采取长期努力的方式,并实施自己的 JsonConverter绝对控制您的属性将如何序列化/反序列化。

您必须将JsonConverterAttribute应用于List<Answer>属性,以告知Json.Net使用您的自定义序列化程序。

但是,如果可以避免,我强烈建议不采用这种方法。

偏离主题作为旁注: 您应该考虑使用CamelCasePropertyNamesContractResolver 告诉序列化程序在序列化您的属性时使用CamelCasing,因此您不必在属性本身上使用camelCasing:Poll.answers应拼写为answers你想遵循常见的命名做法。

答案 1 :(得分:2)

如果您不需要使用序列化进行往返(换句话说,您只需序列化但不需要反序列化),那么获得所需结果的一种简单方法是制作集合类像这样实施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的类视为数组。

如果您还需要反序列化,那么您需要更进一步,并执行以下操作之一:

  1. 添加一个接受IEnumerable<T>的构造函数,如@dbc在下面的评论中所建议的那样;
  2. 让您的班级工具ICollection<T>
  3. 只需让你的Collection类继承自List<T> @Fabio Salvalai在他的回答中建议。
  4. 如果您不想更改类,另一种方法是实现自定义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;
        }
    }
    

    要使用转换器,您需要将[JsonConverter]属性添加到Poll类的Collection属性中,如下所示:

    class Poll
    {
        [JsonConverter(typeof(CollectionConverter<AnswersCollection, Answer>), "answers")]
        public AnswersCollection answers { get; set; }
    
        [JsonConverter(typeof(CollectionConverter<TagsCollection, Tag>), "tags")]
        public TagsCollection tags { get; set; }
    
        public string question { get; set; }
    }
    

    然后像往常一样序列化和反序列化。