2014-05-14 8 views
6

Questo è un esempio del file JSON:come deserializzare un nodo principale denominato in modo dinamico con json.NET

{ 
   "John Smith": { 
      "id": "72389", 
      "email": "[email protected]", 
      "books": [ 
         { 
            "id": "0", 
            "title": "The Hunger Games", 
            "rating": "5" 
         }, 
         { 
            "id": "1", 
            "title": "Harry Potter and the Order of the Phoenix", 
            "rating": "3" 
         }, 
      ], 
      "magazines": [ 
         { 
            "id": "2", 
            "title": "National Geographic", 
            "rating": "1" 
         }, 
         { 
            "id": "3", 
            "title": "Wired", 
            "rating": "4" 
         } 
      ], 
   } 
} 

Avviso il nodo principale ha un nome dinamico (John Smith), e ogni JSON ho bisogno di deserializzare avrà un nome diverso. Tale struttura JSON richiederebbe di avere configurazione classi come segue:

public class RootObject 
{ 
    public JohnSmith { get; set; } 
} 

public class JohnSmith //oops 
{ 
    public string id { get; set; } 
    public string email { get; set; } 
    public List<Book> books { get; set; } 
    public List<Magazine> magazines { get; set; } 
} 

public class Book 
{ 
    public string id { get; set; } 
    public string title { get; set; } 
    public string rating { get; set; } 
} 

public class Magazine 
{ 
    public string id { get; set; } 
    public string title { get; set; } 
    public string rating { get; set; } 
} 

Il mio obiettivo è quello di deserializzare "/ ignorando Annullamento" oggetto radice e, soprattutto, che dynamicaly nome nodo. Questo non è cruciale, ma mi piacerebbe essere in grado di ottenere il cognome e impostare come proprietà sulla classe Person.

public class Person 
{ 
    public string id { get; set; } 
    public string email { get; set; } 
    public string name { get; set; } 
    public List<Book> books { get; set; } 
    public List<Magazine> magazines { get; set; } 
} 

public class Book 
{ 
    public string id { get; set; } 
    public string title { get; set; } 
    public string rating { get; set; } 
} 

public class Magazine 
{ 
    public string id { get; set; } 
    public string title { get; set; } 
    public string rating { get; set; } 
} 

Ecco come sto facendo adesso:

var jo = JObject.Parse(json); 
var deserializable = jo.First.First.ToString(); 

string name; 
var jp = (JProperty)jo.First; 
if (jp != null) name = jp.Name; 

var person = JsonConvert.DeserializeObject<Person>(deserializable); 
person.name = name; 

Questo funziona bene, ma mi chiedevo, forse potrebbe essere fatto meglio utilizzando una consuetudine JsonConverter? Temo che questo sia un po 'eccessivo, quindi ti sto chiedendo un aiuto ...

In ogni caso, se c'è un modo migliore per farlo, per favore condividi.

risposta

8

Manterrei la prima parte della soluzione (deserializzazione su JObject), ma non eseguirò un'altra serializzazione. Il mio codice sarebbe simile a questa:

var jo = JObject.Parse(json); 
var jp = jo.Properties().First(); 
var name = jp.Name; 
var person = jp.Value.ToObject<Person>(); 

Edit:

Nel caso in cui si desidera un convertitore personalizzato, è possibile utilizzare il seguente codice. Il convertitore converte il tuo oggetto in un elenco di Person s in cui ogni proprietà rappresenta un altro Person.

class PersonListConverter : JsonConverter 
{ 
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     var list = (PersonList) value; 

     writer.WriteStartObject(); 

     foreach (var p in list.Persons) 
     { 
      writer.WritePropertyName(p.Name); 
      serializer.Serialize(writer, p); 
     } 

     writer.WriteEndObject(); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     var jo = serializer.Deserialize<JObject>(reader); 
     var result = new PersonList(); 
     result.Persons = new List<Person>(); 

     foreach (var prop in jo.Properties()) 
     { 
      var p = prop.Value.ToObject<Person>(); 
      // set name from property name 
      p.Name = prop.Name; 
      result.Persons.Add(p); 
     } 

     return result; 
    } 

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

Dove PersonList sarebbe simile a questa:

[JsonConverter(typeof(PersonListConverter))] 
class PersonList 
{ 
    public List<Person> Persons { get; set; } 
} 
+0

+1 Mi piace la tua risposta migliore della mia. – codenheim

+0

come posso ottenere il nome della proprietà principale? –

+0

@RoyiNamir Non sono sicuro di cosa vuoi sapere. Vuoi ottenere il nome di quale proprietà esattamente? L'esempio JSON di cryodream ha una sola persona (= una proprietà nell'oggetto principale). Il mio codice itera su tutte le proprietà dell'oggetto principale. Ma usando l'esempio JSON, ci sarà solo un'iterazione, ovviamente. Quindi nel mio codice non c'è _main property_. Tutte le proprietà dell'oggetto radice vengono trattate allo stesso modo (nei cicli 'foreach'). – fero

0

Vorrei provare a utilizzare un'espressione regolare sul JSON non elaborato, estrarre il nome, regex sostituirlo con un nome di nodo fisso, quindi chiamare deserialize sul risultato modificato con il nodo root che ora si conosce.

Problemi correlati