2014-09-04 15 views
5

Ho bisogno di deserializzare json come questo (è come definito in DICOM - uno standard internazionale, quindi non posso cambiare il formato!)Come posso selezionare un tipo di oggetto per deserializzare usando Json.Net in base ai valori di proprietà nei dati

{ 
.... 
"00080060": { 
    "Value": [ 
    "US" 
    ], 
    "vr": "CS" 
}, 
"00080070": { 
    "Value": [ 
    "ACME Products" 
    ], 
    "vr": "LO" 
}, 
"00080090": { 
    "Value": [ 
    { 
     "AlphabeticName": "Better^Make^U.^MD" 
    } 
    ], 
    "vr": "PN" 
}, 
"00081110": { 
    "Value": [ 
    { 
     "00080008": { 
     "Value": [ 
      "XX_0", 
      "OIU", 
      null, 
      "PPP" 
     ], 
     "vr": "CS" 
     } 
    }, 
    {}, 
    { 
     "00080008": { 
     "Value": [ 
      "XX_2", 
      "OIU", 
      null, 
      "PPP" 
     ], 
     "vr": "CS" 
     } 
    } 
    ], 
    "vr": "SQ" 
}, 

Ogni proprietà (di una lunga lista!) ha un nome (0008XXXX nell'esempio sopra), e ha sotto-proprietà "vr" e valore. Nella maggior parte dei casi, Value è semplicemente una matrice di oggetti (stringa o numero) e va bene, ma per 2 casi speciali (PN e SQ come sopra) ha bisogno di una gestione speciale, e nel caso di SQ è in realtà una matrice di di nuovo oggetto di livello superiore (che può essere annidato ricorsivamente all'infinito ...)

Quindi - quello di cui ho bisogno è un metodo semplice, durante la deserializzazione per ispezionare il valore "vr" e per dare a json.net il tipo che dovrebbe usare per il "Valore" associato - esiste un mezzo semplice per farlo? Ovviamente, il fatto che vr possa arrivare prima o dopo Value (a seconda dell'implementazione remota) potrebbe complicare ulteriormente le cose ...

Ho visto i meccanismi ContractResolver e JsonConverter di json.net, ma ContractResolver sembra non darmi accesso ai dati letti per permettere di fare la scelta, e le classi derivate da JsonConverter sembrerebbero lasciarmi dover ri-implementare la maggior parte di ciò che json.net è dong (altrimenti così bene!) per me già :-(

mi sto perdendo un po 'di soluzione più ovvia e semplice qui?

+0

'JObject' è quello che ti serve. Sfortunatamente, non ho abbastanza tempo da dedicare all'impulso. –

risposta

5

Ugh, questo è un formato difficile da lavorare. Tuttavia, dovrebbe essere possibile dare un senso di esso utilizzando un costume 012.. L'utilizzo di un JObject all'interno del convertitore ci proteggerà dalla maggior parte del sollevamento pesante. Ma prima, dobbiamo definire un paio di classi per deserializzare i dati in.

La prima classe che chiamerò Node; questo conterrà l'elenco Value e vr.

class Node 
{ 
    public IList Value { get; set; } 
    public string vr { get; set; } 
} 

La seconda classe è necessaria per contenere gli articoli per il caso "PN".

class PnItem 
{ 
    public string AlphabeticName { get; set; } 
} 

Ecco il codice per il convertitore. Il convertitore può esaminare la proprietà vr e utilizzare tali informazioni per creare il tipo corretto di elenco per Value.

class NodeConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return (objectType == typeof(Node)); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JObject jo = JObject.Load(reader); 
     Node node = new Node(); 
     node.vr = (string)jo["vr"]; 
     if (node.vr == "PN") 
     { 
      node.Value = jo["Value"].ToObject<List<PnItem>>(serializer); 
     } 
     else if (node.vr == "SQ") 
     { 
      node.Value = jo["Value"].ToObject<List<Dictionary<string, Node>>>(serializer); 
     } 
     else 
     { 
      node.Value = jo["Value"].ToObject<List<string>>(serializer); 
     } 
     return node; 
    } 

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

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Si noti che per il caso "SQ" stiamo deserializzazione un List<Dictionary<string, Node>>. Questo copre la struttura ricorsiva. Ogni volta che Json.Net tenta di deserializzare un nodo, richiama il convertitore. Usiamo un Dictionary per gestire il fatto che i nomi delle proprietà possono variare (ad es. "00080070", "00080090", ecc.). Alla radice, dobbiamo anche deserializzare a Dictionary<string, Node> per lo stesso motivo.

Così, di legare tutto insieme, ecco come si deserializzare vostro JSON:

var dict = JsonConvert.DeserializeObject<Dictionary<string, Node>>(json, 
                  new NodeConverter()); 

Ecco una demo: https://dotnetfiddle.net/hsFlxU

+1

Assolutamente geniale, proprio quello di cui avevo bisogno! Ho dovuto modificare un po 'per adattarmi ad alcune delle altre stranezze che avevo omesso per chiarezza (ad es.L'INTERA COSA è un array json), quindi finiamo per usare lo stesso generico a doppio livello anche per il livello più alto, – medconn

+0

. Ho avuto la sensazione che probabilmente alcuni dettagli non erano ancora presenti! Sono felice che tu abbia potuto adattare questa soluzione alle tue esigenze. –

Problemi correlati