JSON.net - поле является либо строковым, либо List <string>

У меня есть ситуация, когда JSON, возвращаемый из службы REST, возвращает список объектов Movie, все из которых содержат тонну информации. Пара полей в этом REST -служебном результате изменяется в зависимости от доступной информации.

Пример. У фильма всегда есть некоторые снимки экрана (изображения), актеры и режиссеры. В зависимости от рассматриваемого фильма могут быть одно или несколько изображений, один или несколько участников и один или несколько режиссеров. Пример JSON для нескольких случаев:

{
    "title": "Movie title",
    "images": [
        "http://www.url.com/img_0.jpg",
        "http://www.url.com/img_1.jpg",
        "http://www.url.com/img_2.jpg",
        "http://www.url.com/img_3.jpg",
        "http://www.url.com/img_4.jpg"
    ],
    "actors": [
        "Steven Berkoff",
        "Nikolaj Coster-Waldau",
        "Julie Cox"
    ],
    "directors": "Simon Aeby"
},
{
    "title": "Another movie",
    "images": "http://www.url.com/img_1.jpg",
    "actors": "actor 1"
    "directors": [
        "Justin Bieber",
        "Justin Timberlake"
    ]
}

Вопрос заключается в использовании JSON.net, как я могу создать конвертер, который занимается этой проблемой? Я прочесываю интернет, но до сих пор не нашел решения.

Другое направление по тому же вопросу: если поле является либо списком строк, либо простой строкой, как заставить JSON.NET создавать список в любом случае (и если просто простая строка, создайте список с одним членом )

EDIT: эта служба REST находится вне моего контроля

Ответы

Ответ 1

Хорошо, я сделал это для удовольствия, но не думаю, что это полезно или лучший способ, во всяком случае...

Объявление "динамических" атрибутов как объекта, а затем создание методов для получения свойств как-то вроде ImagesAsList или ImagesAsString. Я сделал это с помощью методов расширения.....

var movies = JsonConvert.DeserializeObject<List<Movie>>(str);

Класс

class Movie
{

    [JsonProperty("title")]
    public string Title { get; set; }

    [JsonProperty("images")]
    public object Images { get; set; }

    [JsonProperty("actors")]
    public object Actor { get; set; }

    [JsonProperty("directors")]
    public object Directors { get; set; }
}

Методы расширения

static class MovieExtension
{
    public static List<string> ImagesAsList(this Movie m)
    {
        var jArray = (m.Images as JArray);
        if (jArray == null) return null;

        return jArray.Select(x => x.ToString()).ToList();
    }

    public static string ImagesAsString(this Movie m)
    {
        return m.Images as string;
    }

}

Забастовкa >

EDIT

После чтения комментариев @yamen я сделал некоторые изменения, например:

var settings = new JsonSerializerSettings();
settings.Converters.Add(new MoviesConverter());

var movies = JsonConvert.DeserializeObject<List<Movie>>(str, settings);

Класс

class Movie
{

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

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

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

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

Преобразователь

class MoviesConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(string)) || (objectType == typeof(List<string>)) ;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartArray)
        {
            var l = new List<string>();
            reader.Read();
            while (reader.TokenType != JsonToken.EndArray)
            {
                l.Add(reader.Value as string);

                reader.Read();
            }
            return l;
        }
        else
        {
            return new List<string> { reader.Value as string };
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        //ToDo here we can decide to write the json as 
        //if only has one attribute output as string if it has more output as list
    }
}

Ответ 2

Вы не сможете сериализоваться непосредственно на объект, но вы можете сделать это вручную без особых усилий. JSON.Net содержит LINQ to JSON. Сначала определите метод, который всегда будет возвращать список типов T, даже если базовый JSON не является массивом:

public List<T> getSingleOrArray<T>(JToken token)
{
    if (token.HasValues)
    {
        return token.Select(m => m.ToObject<T>()).ToList();
    }
    else
    {
        return new List<T> { token.ToObject<T>() };
    }
}

Использование образца:

JObject m1 = JObject.Parse(@"{
""title"": ""Movie title"",
""images"": [
    ""http://www.url.com/img_0.jpg"",
    ""http://www.url.com/img_1.jpg""
],
""actors"": [
    ""Steven Berkoff"",
    ""Julie Cox""
],
""directors"": ""Simon Aeby""
}");

JObject m2 = JObject.Parse(@"{
""title"": ""Another movie"",
""images"": ""http://www.url.com/img_1.jpg"",
""actors"": ""actor 1"",
""directors"": [
    ""Justin Bieber"",
    ""Justin Timberlake""
]
}");

IList<String> m1_directors = getSingleOrArray<string>(m1["directors"]);
IList<String> m2_directors = getSingleOrArray<string>(m2["directors"]);

m1_directory - это список с одним элементом, m2_directors - это список с двумя элементами.