2016-05-01 20 views
9

sono incappato in un servizio che emette JSON nel seguente formato:Json.NET personalizzato JsonConverter con tipi di dati

{ 
    "Author": "me", 
    "Version": "1.0.0", 
    "data.Type1": { 
     "Children": [ 
      { 
       "data.Type1": { 
        "Children": [ 
         { 
          "data.Type2": { 
           "name": "John", 
           "surname": "Doe" 
          } 
         } 
        ] 
       } 
      }, 
      { 
       "data.Type3": { 
        "dob": "1990-01-01" 
       } 
      } 
     ] 
    } 
} 

nomi dei tipi di dati sono conservati come nomi di proprietà ed i loro valori sono gli oggetti reali. Iniziano tutti con un prefisso data..

Quello che mi piacerebbe avere in seguito è qualcosa di simile:

{ // Root 
    "Author": "me", 
    "Version": "1.0.0", 
    "Children": [ // Type1 
     { 
      "Children": [ // Type1 
       { // Type2 
        "Name": "John", 
        "Surname": "Doe" 
       } 
      ] 
     }, 
     { // Type3 
      "DoB": "1990-01-01" 
     } 
    ] 
} 

con le seguenti classi:

class Type1 { 
    ICollection<object> Children { get; set; } 
} 

class Type2 { 
    public string Name { get; set; } 
    public string Surname { get; set; } 
} 

class Type3 { 
    public DateTime DoB { get; set; } 
} 

class Root 
{ 
    public string Author { get; set; } 
    public string Version { get; set; } 
    public Type1 Children { get; set; } 
} 

Domanda

Come posso deserializzare questo in aggiunta Classi C#, tenendo conto dei tipi di dati e rimuovendoli dall'albero?

Ho provato con una personalizzazione JsonConverter, ma sto lottando con il modo di scegliere dinamicamente il convertitore, poiché il modo più semplice sarebbe mettere un attributo sulla proprietà, ma non è supportato.

Un piccolo esempio sarebbe fantastico.

risposta

8

Anche se questo formato JSON è un po 'insolito e resiste l'uso di attributi a causa dei nomi delle proprietà dinamiche, è ancora possibile per effettuare una deserializzazione nella struttura di classe preferita con una piccola modifica: ti consigliamo di modificare la proprietà nella classe Root in modo da rispecchiare la proprietà Children nella 210 classe. Come ora, non corrisponde alla struttura dell'output desiderato (dove Children viene visualizzato come una matrice, non un oggetto) e altrimenti richiederebbe codice aggiuntivo nel convertitore per gestire correttamente.

class Root 
{ 
    public string Author { get; set; } 
    public string Version { get; set; } 
    public ICollection<object> Children { get; set; } 
} 

Ecco quello che mi è venuta per il convertitore (assumendo che il cambiamento di cui sopra è fatto):

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JObject obj = JObject.Load(reader); 
     Root root = new Root(); 
     root.Author = (string)obj["Author"]; 
     root.Version = (string)obj["Version"]; 
     root.Children = ((Type1)DeserializeTypeX(obj, serializer)).Children; 
     return root; 
    } 

    private object DeserializeTypeX(JObject obj, JsonSerializer serializer) 
    { 
     JProperty prop = obj.Properties().Where(p => p.Name.StartsWith("data.")).First(); 
     JObject child = (JObject)prop.Value; 
     if (prop.Name == "data.Type1") 
     { 
      List<object> children = new List<object>(); 
      foreach (JObject jo in child["Children"].Children<JObject>()) 
      { 
       children.Add(DeserializeTypeX(jo, serializer)); 
      } 
      return new Type1 { Children = children }; 
     } 
     else if (prop.Name == "data.Type2") 
     { 
      return child.ToObject<Type2>(serializer); 
     } 
     else if (prop.Name == "data.Type3") 
     { 
      return child.ToObject<Type3>(serializer); 
     } 
     throw new JsonSerializationException("Unrecognized type: " + prop.Name); 
    } 

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

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

Armati di questo convertitore è possibile deserializzare alle classi in questo modo:

Root root = JsonConvert.DeserializeObject<Root>(json, new CustomConverter()); 

È quindi possibile serializzare nel nuovo formato come questo:

JsonSerializerSettings settings = new JsonSerializerSettings 
{ 
    DateFormatString = "yyyy-MM-dd", 
    Formatting = Formatting.Indented 
}; 

Console.WriteLine(JsonConvert.SerializeObject(root, settings)); 

Fiddle: https://dotnetfiddle.net/ESNMLE

0

Non so se funzionerà, ma avete provato a utilizzare Newtonsoft.Json per serializzare l'oggetto e includere i tag JsonProperty nelle proprietà della classe? So che funzionerà quando deserializzare Json in una classe.

<JsonProperty("user_id")> 
Public Property UserID As String 
//Converts Json {user_id: 123} to class.UserID = 123 

per serializzare con Newtonsoft, prima includere Newtonsoft nel progetto poi

Dim stringJson As String = Newtonsoft.Json.JsonConvert.SerializeObject(root) 
+0

Grazie per la risposta.Ho provato, ma il problema è che tutti i livelli devono essere rimossi e sostituiti, perché servono solo come dati per distinguere il tipo che rappresentano, dove JsonProperty è solo per mappare tra il nome della classe e il nome della proprietà json. –