2017-01-07 2 views
2

Я получаю следующее из полезной информации json из запроса REST.Как десериализовать корневой объект с бессмысленными именами динамических клавиш? Json.NET

{ 
    "version": "1.1", 
    "date": "2017-01-06", 
    "count": "130", 
    "0": { 
      "artist": "Artist 1", 
      "title": "Title 1" 
    }, 
    "1": { 
      "artist": "Artist 2", 
      "title": "Title 2" 
    }, 
    ... 
    "49": { 
      "artist": "Artist 50", 
      "title": "Title 50" 
    } 
} 

Как вы можете видеть, существует несколько числовых элементов корня. Эти элементы индексируют объект в ответе, поэтому мне не нужны сами цифры, мне просто нужен объект Song, который они представляют. Мне также нужен count, чтобы узнать, нужно ли мне позвонить на сервер, чтобы получить следующую страницу песен (сервер возвращает 50 Song объектов максимум за звонок).

Вот что у меня есть для Song класса:

public class Song 
{ 
    public string artist {get; set;} 
    public string title {get; set;} 
} 

Но я, имеющие вопросы, получить корневой элемент. Я попытался подражать процессу, используемому при динамическом дочернем элементе, но это не работает.

public class SongsResponse 
{ 
    public string version { get; set; } 
    public string date { get; set; } 
    public string count {get; set; } 
    public Dictionary<string, Song> songs { get; set; } 
} 

Я видел решения, которые «выбрасывают» version, date и count и другие, которые делают это без заранее определенных классов.

Но мне нужно count и в идеале хотелось бы работать с предопределенными классами.

+0

Голосов без комментариев ??? – dbJones

+0

Реорганизация опции? Зачем вам нужно? Если 'песни' реализует' ICollection', у него будет свойство 'Count'. –

+0

Сервер возвращает только 50 ответов за раз. Но 'count' говорит вам, насколько подходят критерии. Поэтому 'count' может быть' 250'. Обновленный вопрос, чтобы сделать это понятнее. – dbJones

ответ

1

Вам не нужно включать индекс. Песни индексируются в силу того, что они будут десериализованы в коллекцию. Измените схему следующим образом:

{ 
    "version":"1.1", 
    "date":"2017-01-06", 
    "count":"130", 
    "songs":[ 
     { 
     "artist":"Artist 1", 
     "title":"Title 1" 
     }, 
     { 
     "artist":"Artist 2", 
     "title":"Title 2" 
     }, 
     { 
     "artist":"Artist 3", 
     "title":"Title 3" 
     } 
    ] 
} 

public class Song 
{ 
    public string artist {get; set;} 
    public string title {get; set;} 
} 

public class SongsResponse 
{ 
    public string version { get; set; } 
    public string date { get; set; } 
    public string count {get; set; } 
    public List<Song> songs { get; set; } 
} 

Update

Таким образом, вы не можете иметь никакого контроля над JSON, посылаемый к вам, но вы до сих пор контроль над локальными объектами домена и как вы десериализации JSON. Я бы придерживался определений классов, определенных выше, и создал пользовательский JsonConverter, который вы можете использовать для десериализации ответа во что-то, что имеет смысл. Фактически, пока вы на нем, вы можете сделать свой класс SongsResponse «лучше», используя соответствующий тип для свойств date и count (зачем использовать сильно типизированный язык, если вы не собираетесь его использовать).

Так этот класс:

public class SongsResponseJsonConverter : JsonConverter 
{ 
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException("Unnecessary because CanWrite is false. The type will skip the converter."); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     var result = new SongsResponse(); 

     var obj = JObject.Load(reader); 
     result.version = obj["version"].Value<string>(); 
     result.date = obj["date"].Value<DateTime>(); 
     result.count = obj["count"].Value<int>(); 
     result.songs = 
      obj 
      .Properties() 
      .Where(x => !(new string[] { "version", "date", "count" }.Contains(x.Name))) 
      .Select(x => new { Id = int.Parse(x.Name), Song = x }) 
      .OrderBy(x => x.Id) 
      .Select(x => x.Song.Value.ToObject<Song>()) 
      .ToList(); 
     return result; 
    } 

    public override bool CanRead 
    { 
     get { return true; } 
    } 

    public override bool CanWrite 
    { 
     get { return false; } 
    } 

    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(SongsResponse); 
    } 
} 

Что вы будете использовать, как это:

var json = @" 
{ 
    ""version"": ""1.1"", 
    ""date"": ""2017-01-06"", 
    ""count"": ""130"", 
    ""0"": { 
     ""artist"": ""Artist 1"", 
      ""title"": ""Title 1"" 
    }, 
    ""1"": { 
     ""artist"": ""Artist 2"", 
      ""title"": ""Title 2"" 
    }, 
    ""49"": { 
     ""artist"": ""Artist 50"", 
      ""title"": ""Title 50"" 
    } 
}"; 

var response = JsonConvert.DeserializeObject<SongsResponse>(json, new SongsResponseJsonConverter()); 
+0

У меня создается впечатление, что кто-то голосует каждый вопрос и ответ сегодня. –

+0

У меня тоже такое впечатление ... – dbJones

+0

К сожалению, я не могу изменить схему. Это ответ, который мне дал сервер вне моего контроля. – dbJones

0

можно десериализации постоянную часть JSON с использованием общих способов, а затем построить динамическую часть USIN JSON2LINQ. В вашем конкретном случае это выглядит следующим образом:

var json = JObject.Parse(responseString); 

var response = json.ToObject<SongsResponse>(); 
response.songs = json.Properties().Where(t => Regex.Match(t.Name, "\\d+").Success).ToDictionary(t => t.Name, t => t.Value.ToObject<Song>()); 

ПРИМЕЧАНИЕ: Под капотом Regex.Match кэширует объект регулярного выражения после первого звонка и все последующие вызовов повторно использовать этот объект.

Смежные вопросы