C # JSON.NET – Deserializza la risposta che utilizza una struttura di dati insolita

Sto avendo qualche problema nel trovare un modo pulito (il più ansible) per deserializzare alcuni dati JSON in un formato particolare. Voglio deserializzare i dati per classi di oggetti dati fortemente tipizzati, abbastanza flessibili per quanto riguarda le specifiche di questo. Ecco un esempio di come appaiono i dati:

{ "timestamp": 1473730993, "total_players": 945, "max_score": 8961474, "players": { "Player1Username": [ 121, "somestring", 679900, 5, 4497, "anotherString", "thirdString", "fourthString", 123, 22, "YetAnotherString"], "Player2Username": [ 886, "stillAstring", 1677, 1, 9876, "alwaysAstring", "thirdString", "fourthString", 876, 77, "string"] } } 

Le parti specifiche di cui non sono sicuro sono:

  1. La raccolta di giocatori sarebbe considerata un dizionario? Il nome utente può fungere da chiave, ma il valore mi sta allontanando poiché sarebbe una raccolta mista di valori stringa e interi.
  2. Un giocatore è composto interamente da valori senza nome. Ho praticamente sempre lavorato con dati JSON che avevano denominato proprietà e valori (ad esempio timestamp, total_players, ecc. In alto)

Dì che ho una class di alto livello come questa:

 public class ScoreboardResults { public int timestamp { get; set; } public int total_players { get; set; } public int max_score { get; set; } public List players { get; set; } } 

Che aspetto avrebbe l’object Player dato che è fondamentalmente una chiave / valore con il nome utente che funge da chiave e il valore è una raccolta di interi e stringhe misti? I dati per ciascun elemento del giocatore sono sempre nello stesso ordine, quindi so che il primo valore nella raccolta è il loro UniqueID, il secondo valore è una descrizione del giocatore, ecc. Vorrei che la class del giocatore fosse qualcosa del genere:

 public class Player { public string Username { get; set; } public int UniqueID { get; set; } public string PlayerDescription { get; set; } .... .... .... Following this pattern for all of the values in each player element .... .... } 

Sono sicuro che questa è una cosa abbastanza semplice da fare con JSON.NET, ed è per questo che ho voluto evitare le idee che avevo su come ottenere questo risultato. Ciò che mi è venuto in mente sarebbe stato poco elegante e probabilmente sobject ad errori in una certa misura durante il processo di serializzazione.

MODIFICARE

Ecco le classi che vengono generate quando si utilizza il passato come classi JSON come suggerito da snow_FFFFFF :

 public class Rootobject { public int timestamp { get; set; } public int total_players { get; set; } public int max_score { get; set; } public Players players { get; set; } } public class Players { public object[] Player1Username { get; set; } public object[] Player2Username { get; set; } } 

Ciò che non mi è chiaro è come faccio a deserializzare i dati JSON nell’elemento “players” come una lista con Player1Username che è una semplice proprietà stringa sull’object Player. Per quanto riguarda la raccolta di stringhe e interi intermix, sono sicuro di poterli inserire in proprietà individuali sull’object Player senza problemi.

Il convertitore da Deserializing JSON in Visual Basic .NET dovrebbe fare ciò di cui hai bisogno, opportunamente tradotto da VB.NET in c #:

 public class ObjectToArrayConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(T) == objectType; } static bool ShouldSkip(JsonProperty property) { return property.Ignored || !property.Readable || !property.Writable; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var type = value.GetType(); var contract = serializer.ContractResolver.ResolveContract(type) as JsonObjectContract; if (contract == null) throw new JsonSerializationException("invalid type " + type.FullName); var list = contract.Properties.Where(p => !ShouldSkip(p)).Select(p => p.ValueProvider.GetValue(value)); serializer.Serialize(writer, list); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var token = JArray.Load(reader); var contract = serializer.ContractResolver.ResolveContract(objectType) as JsonObjectContract; if (contract == null) throw new JsonSerializationException("invalid type " + objectType.FullName); var value = existingValue ?? contract.DefaultCreator(); foreach (var pair in contract.Properties.Where(p => !ShouldSkip(p)).Zip(token, (p, v) => new { Value = v, Property = p })) { var propertyValue = pair.Value.ToObject(pair.Property.PropertyType, serializer); pair.Property.ValueProvider.SetValue(value, propertyValue); } return value; } } 

Successivamente, aggiungi il convertitore alla tua class Player e indica l’ordine di ogni proprietà utilizzando JsonPropertyAttribute.Order :

 [JsonConverter(typeof(ObjectToArrayConverter))] public class Player { [JsonProperty(Order = 1)] public int UniqueID { get; set; } [JsonProperty(Order = 2)] public string PlayerDescription { get; set; } // Other fields as required. } 

Quindi, alla fine, dichiara il tuo object radice come segue:

 public class ScoreboardResults { public int timestamp { get; set; } public int total_players { get; set; } public int max_score { get; set; } public Dictionary players { get; set; } } 

Nota che ho spostato il nome Username dalla class Player e nel dizionario, come chiave.

Un buon modo per iniziare sarebbe lasciare che lo studio visivo generi la tua class sulla base del JSON. Aprire un file di class vuoto e andare su MODIFICA -> INCOLLA SPECIALE -> INCOLLA JSON come CLASSI.

Questo genererà un file con le classi necessarie per serializzare / deserializzare il tuo JSON.

Prova questo

Crea una class come di seguito

Nota: puoi usare l’opzione Incolla speciale in Visual Studio per generare tutte le classi relative al JSON

Modifica -> Incolla speciale -> Incolla Json As Classes

creerà tutte le classi relative al JSON

Nota: riferimento a questo ho già risposto simile come questo ..