将成员序列化为Object或List <object> </object>

时间:2014-01-23 07:20:49

标签: c# json.net

这有点类似于JSON.net - field is either string or List

让我们说我在POCO类之下,需要将json字符串反序列化为该类型。

public class Movie 
{
    [JsonProperty("title")]
    public List<string> Title { get; set; }

    [JsonProperty("images")]
    public List<Image> Images { get; set; }

    [JsonProperty("actors")]
    public List<Actor> Actors { get; set; }

    [JsonProperty("directors")]
    public List<Director> Directors { get; set; }    
}

让我们说需要反序列化的json字符串如下

{
    "title": "Movie title",
    "images": [ 
                {"url" : "http://www.url.com/img_3.jpg"},
                {"url" : "http://www.url.com/img_4.jpg"}
              ],
    "actors": [ 
                {"name" : "Steven Berkoff"},
                {"name" : "Nikolaj Coster-Waldau"},
                {"name" : "Julie Cox"}
              ],
    "directors": { "name" : "Simon Aeby" }
},
{  
    "title": "Movie title",
    "images": {"url" : "http://www.url.com/img_3.jpg"},                  
    "actors": {"name" : "Julie Cox"},                 
    "directors": { "name" : "Simon Aeby" }
}

这里的问题甚至认为POCO期待一个导演,演员和图像的列表,json只有每种类型中的一个。尝试反序列化时会出错。

1 个答案:

答案 0 :(得分:2)

为那些列表创建一个转换器,可以识别列表中是否有一个或多个项目并将其应用于您的属性:

public class SingleOrEnumerableJsonConverter<TEnumerable> : JsonConverter
        where TEnumerable : IEnumerable
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(TEnumerable).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var obj = serializer.Deserialize<JToken>(reader);
        return ConvertObject(obj) ?? GetDefaultValue();
    }

    private object GetDefaultValue()
    {
        return Activator.CreateInstance<TEnumerable>();
    }

    private object ConvertObject(JToken obj)
    {
        try
        {
            return obj.ToObject<TEnumerable>();
        }
        catch (JsonSerializationException)
        {
            // try as an array of object
            return new JArray { obj }.ToObject<TEnumerable>();
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        object serializableValue = GetSerializableValue((TEnumerable)value, serializer);
        serializer.Serialize(writer, serializableValue);
    }

    private object GetSerializableValue(TEnumerable items, JsonSerializer serializer)
    {
        var arr = new JArray(items.Cast<object>()
            .Select(item => JToken.FromObject(item, serializer))
        );
        return arr.Count > 1 ? arr : arr.SingleOrDefault();
    }
}
public class Movie 
{
    [JsonProperty("title")]
    [JsonConverter(typeof(SingleOrEnumerableJsonConverter<List<string>>))]
    public List<string> Title { get; set; }

    [JsonProperty("images")]
    [JsonConverter(typeof(SingleOrEnumerableJsonConverter<List<Image>>))]
    public List<Image> Images { get; set; }

    [JsonProperty("actors")]
    [JsonConverter(typeof(SingleOrEnumerableJsonConverter<List<Actor>>))]
    public List<Actor> Actors { get; set; }

    [JsonProperty("directors")]
    [JsonConverter(typeof(SingleOrEnumerableJsonConverter<List<Director>>))]
    public List<Director> Directors { get; set; }    
}