2017-01-07 2 views
2

Ich bekomme Folgendes aus Json Payload von einer REST-Anfrage.Wie deserialize Root-Objekt mit sinnlosen dynamischen Schlüsselnamen? 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" 
    } 
} 

Wie Sie sehen können, gibt es mehrere numerische Wurzelelemente. Diese Elemente indizieren das Objekt in der Antwort, also brauche ich die Zahlen nicht selbst, ich brauche nur das Song Objekt, das sie darstellen. Ich brauche auch count, um zu wissen, ob ich einen Anruf beim Server machen muss, um die nächste Seite der Lieder zu erhalten (der Server gibt 50 Song Objekte pro Anruf zurück).

Hier ist, was ich für Song Klasse:

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

Aber ich habe Probleme das Wurzelelement zu bekommen. Ich habe versucht, den Prozess nachzuahmen, der verwendet wird, wenn ein dynamisches untergeordnetes Element vorhanden ist, aber das funktioniert nicht.

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; } 
} 

Ich habe Lösungen gesehen, die „Wegwerf“ version, date und count und andere, die es ohne vordefinierte Klassen zu tun.

Aber ich brauche count und idealerweise würde von vordefinierten Klassen arbeiten wollen.

+0

Down Abstimmung ohne Kommentar ?? – dbJones

+0

Refactoring eine Option? Warum musst du zählen? Wenn 'songs' 'ICollection' implementiert, wird es eine 'Count'-Eigenschaft haben. –

+0

Server gibt nur 50 Antworten gleichzeitig zurück. Aber "count" sagt Ihnen, wie viele die Kriterien erfüllen. So könnte 'count'' 250' sein. Aktualisierte Frage, um das klarer zu machen. – dbJones

Antwort

1

Sie müssen keinen Index angeben. Die Lieder sind indexiert aufgrund der Tatsache, dass sie zu einer Sammlung deserialisiert werden. Ändern Sie Ihr Schema wie folgt:

{ 
    "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; } 
} 

aktualisieren

So können Sie keine Kontrolle über die json haben wird Ihnen geschickt, aber sie haben immer noch die Kontrolle über Ihre lokale Domain-Objekte und wie Sie deserialisieren die json. Ich würde bei den Klassendefinitionen bleiben, die ich oben definiert habe, und eine benutzerdefinierte JsonConverter erstellen, die Sie verwenden können, um die Antwort in etwas zu deserialisieren, das ein wenig mehr Sinn macht. Während Sie gerade dabei sind, können Sie Ihre SongsResponse Klasse "besser" machen, indem Sie den entsprechenden Typ für die Eigenschaften date und count verwenden (warum verwenden Sie eine stark typisierte Sprache, wenn Sie diese nicht nutzen).

Also diese Klasse:

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); 
    } 
} 

, die Sie mögen diese verwenden würden:

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

Ich habe den Eindruck, dass jemand heute jede Frage ablehnt und beantwortet. –

+0

Ich bekomme diesen Eindruck auch ... – dbJones

+0

Leider kann ich das Schema nicht ändern. Dies ist eine Antwort, die mir von einem Server außerhalb meiner Kontrolle gegeben wurde. – dbJones

0

Es ist möglich, konstante Teil der JSON deserialisieren gemeinsame Wege und anschließend dynamischen Teil usin JSON2LINQ zu konstruieren. In Ihrem speziellen Fall sieht es wie folgt aus:

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>()); 

HINWEIS: Unter der Haube Regex.Match Caches Regex Objekt nach dem ersten Aufruf und alle nachfolgenden Anrufe Wiederverwendung dieses Objekt.

Verwandte Themen