2012-06-11 12 views
6

Sto lavorando con un'API esterna che restituisce una proprietà come matrice o come oggetto, a seconda del conteggio. Qual è un buon modo per gestire questo?Analizza oggetto JSON non array come array con Json.net

Tornando come array:

{ 
    "contacts": { 
     "address": [ 
      { 
       "id": "47602070", 
       "type": "Work", 
       "street": "MyStreet", 
       "city": "MyCity", 
       "zip": "12345", 
       "country": "USA" 
      }, 
      { 
       "id": "47732816", 
       "type": "GPS", 
       "street": "50.0,30.0" 
      } 
     ] 
    } 
} 

Tornando come oggetto:

{ 
    "contacts": { 
     "address": { 
      "id": "47602070", 
      "type": "Work", 
      "street": "MyStreet", 
      "city": "MyCity", 
      "zip": "12345", 
      "country": "USA" 
     } 
    } 
} 

sto pensando una soluzione sarebbe quella di utilizzare un deserializzatore costume e restituire un array di lunghezza 1 per il caso dell'oggetto e deserializzazione predefinita per il caso dell'array, ma non so ancora come farlo.

Ho provato a deserializzare l'oggetto in un array e sperare che Json.net gestisse questo caso per me, ma senza dadi.

risposta

3

Un convertitore JSON.NET personalizzato potrebbe fare il trucco qui. Non è così difficile.

Per una proprietà DateTime si potrebbe fare come segue. Basta decorare la proprietà in questione con il convertitore personalizzato.

[JsonObject(MemberSerialization.OptIn)] 
public class MyClass 
{ 
    [JsonProperty(PropertyName = "creation_date")] 
    [JsonConverter(typeof(UnixDateTimeConverter))] 
    public DateTime CreationDate { get; set; } 
} 

JSON.NET fornisce la maggior parte degli impianti idraulici. Deriva da un convertitore di base.

public class UnixDateTimeConverter : DateTimeConverterBase 
{ 
    public override void WriteJson(JsonWriter writer, object value, 
            JsonSerializer serializer) 
    { ...} 

    public override void WriteJson(JsonWriter writer, object value, 
            JsonSerializer serializer) 
    { ... } 
} 

Tutto quello che dovete fare è implementare il ReadJson (deserializzazione) e metodi WriteJson (serializzazione).

è possibile trovare un esempio completo qui:

Writing a custom Json.NET DateTime Converter

per il vostro problema particolare è necessario un po 'più di controllo. Provate il seguente tipo di convertitore:

public class Contact 
{ 
    private List<Address> _addresses = new List<Address>();  
    public IEnumerable<Address> Addresses { get { return _addresses; } 
} 

public class ContactConverter : CustomCreationConverter<Contact> 
{ 
    public override Contact Create(Type objectType) 
    { 
     return new Contact(); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object 
     existingValue, JsonSerializer serializer) 
    { 
     var mappedObj = new Contact(); 

     // Parse JSON data here 
     // ... 

     return mappedObj; 
    } 
} 

Utilizzando un convertitore personalizzato come quello di cui sopra è possibile analizzare i dati JSON te stesso e comporre l'oggetto contatto (s) come ti pare.

ho modificato un esempio che ho trovato qui:

JSON.NET Custom Converters–A Quick Tour

In questo caso è necessario passare il convertitore personalizzato quando desearilizing.

Contact contact = 
    JsonConvert.DeserializeObject<Contact>(json, new ContactConverter()); 
+0

Grazie per l'input approfondito. Dal tuo esempio l'ho fatto funzionare e pubblicato il mio risultato finale. – angularsen

+0

Se si minimizza almeno commento perché ... non colpire ed eseguire –

+0

Grazie per il collegamento. :) –

5

Sulla base di Christophe Geers' answer, qui è quello che ho finito per fare.

  1. Creare un convertitore JSON personalizzato per analizzare sempre il JSON come array. Se il JSON è un oggetto non matrice, deserializzare l'oggetto e avvolgerlo in una matrice.

  2. Contrassegnare le proprietà corrispondenti con un attributo di convertitore personalizzato.

convertitore personalizzato

public class JsonToArrayConverter<T> : CustomCreationConverter<T[]> 
{ 
    public override T[] Create(Type objectType) 
    { 
     // Default value is an empty array. 
     return new T[0]; 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object 
     existingValue, JsonSerializer serializer) 
    { 

     if (reader.TokenType == JsonToken.StartArray) 
     { 
      // JSON object was an array, so just deserialize it as usual. 
      object result = serializer.Deserialize(reader, objectType); 
      return result; 
     } 
     else 
     { 
      // JSON object was not an arry, so deserialize the object 
      // and wrap it in an array. 
      var resultObject = serializer.Deserialize<T>(reader); 
      return new T[] {resultObject}; 
     } 
    } 
} 

Strutture dati per l'esempio domanda

public class Organisation 
{ 
    public Contacts contacts; 
} 

public class Address 
{ 
    public string id; 
    public string street; 
    public string city; 
    public string type; 
    public string zip; 
    public string country; 
} 

public class Contacts 
{ 
    // Tell JSON.net to use the custom converter for this property. 
    [JsonConverter(typeof(JsonToArrayConverter<Address>))] 
    public Address[] address; 
} 
1

Nota: Invece di utilizzare un CustomCreationConverter, si può semplicemente utilizzare un convertitore ordinario. Per esempio io uso qualcosa del genere:

public class SingleToArrayConverter<T> : JsonConverter 
{ 
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     var items = (IEnumerable<T>)value; 
     if (value == null || !items.Any()) 
     { 
      writer.WriteNull(); 
     } 
     else if (items.Count() == 1) 
     { 
      serializer.Serialize(writer, items.ElementAt(0)); 
     } 
     else 
     { 
      serializer.Serialize(writer, items); 
     } 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (!CanConvert(objectType)) 
     { 
      throw new NotSupportedException(); 
     } 

     if (reader.TokenType == JsonToken.Null) 
     { 
      reader.Skip(); 
      return null; 
     } 
     else if (reader.TokenType == JsonToken.StartObject) 
     { 
      return new T[] { serializer.Deserialize<T>(reader) }; 
     } 
     else 
     { 
      return serializer.Deserialize<T[]>(reader); 
     } 
    } 

    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(IEnumerable<T>); 
    } 
}