2013-07-19 6 views
6

Ho un codice .NET che deserializza gli oggetti JSON creati da un webservice con un linguaggio dinamico. Poiché la sorgente è dinamica, a volte serializza i valori interi in formato float (ad esempio, il numero 2 viene serializzato su "2.0").Come posso ripristinare il comportamento di deserializzazione dopo l'aggiornamento di Json.NET?

Con Json.NET 4.0.4, questo ha funzionato perfettamente (sembra che l'arrotondamento sia stato applicato durante la deserializzazione). Con l'aggiornamento a Json.NET 4.5, tuttavia, la deserializzazione 2.0 ora genera un valore FormatException. Ecco il codice:

// works as expected in both versions 
var s = "2"; 
Console.WriteLine(JsonConvert.DeserializeObject<int>(s)); 

// throws FormatException in 4.5 only 
var s = "2.0"; 
Console.WriteLine(JsonConvert.DeserializeObject<int>(s)); 

// throws FormatException in 4.5, rounds to 3 in 4.0.4 
var s = "2.6"; 
Console.WriteLine(JsonConvert.DeserializeObject<int>(s)); 

Esiste un modo semplice per ripristinare il comportamento originale? Il comportamento ideale sarebbe deserializzare solo numeri con valori interi, ma in qualsiasi formato (ad esempio 2.0, 1e10, ma non 2.5), ma mi accontento del comportamento 4.0.4.

risposta

6

È possibile farlo facendo una consuetudine JsonConverter che gestirà l'arrotondamento (o scartare) i valori decimali. Potrebbe sembrare qualcosa di simile:

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JValue jsonValue = serializer.Deserialize<JValue>(reader); 

     if (jsonValue.Type == JTokenType.Float) 
     { 
      return (int)Math.Round(jsonValue.Value<double>()); 
     } 
     else if (jsonValue.Type == JTokenType.Integer) 
     { 
      return jsonValue.Value<int>(); 
     } 

     throw new FormatException(); 
    } 

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

è possibile utilizzare il convertitore personalizzato come questo:

JsonSerializerSettings settings = new JsonSerializerSettings 
{ 
    Converters = new List<JsonConverter> { new CustomIntConverter() } 
}; 

string json = @"[2.6, 0, 4.1, 5, -3, -2.2]"; 

List<int> list = JsonConvert.DeserializeObject<List<int>>(json, settings); 

foreach (int val in list) 
{ 
    Console.WriteLine(val); 
} 

L'uscita di quanto sopra sarebbe questo:

3 
0 
4 
5 
-3 
-2 

Se si farebbe piuttosto il convertitore ignora valori decimali anziché intorno ad esse, sostituire la seguente riga di codice

 return (int)Math.Round(jsonValue.Value<double>()); 

con questo:

 return (existingValue ?? default(int)); 

Dopo aver effettuato questo cambiamento, l'uscita del codice di prova di cui sopra sarebbe quindi simile a questa:

0 
0 
0 
5 
-3 
0 
+0

potrebbe essere necessario prendere in considerazione l'aggiunta di 'objectType == typeof (oggetto) in 'CanConvert', se la classe di destinazione" non specifica "il tipo attuale es 'classe MyObject {Id oggetto pubblico; } ' – drzaus

+0

@drzaus Forse, ma attenzione. Se lo fai e hai altre proprietà di tipo 'object' che sono * non * numeri, questo convertitore proverà a gestirli, il che potrebbe non essere quello che ti aspetti. Dovresti aggiungere del codice per gestire quel caso. –

+0

Ah giusto, stavo pensando di alcuni [altre risposte] (http://stackoverflow.com/a/28748973/1037948) che gestirlo controllando la 'JTokenType' e se non è un tipo previsto, piuttosto che gettando un' FormatException' restituisce solo 'serializer.Deserialize (reader)', che sembra delegarlo a qualcos'altro che dovrebbe gestirlo appropriatamente. Ha funzionato finora nei miei test con tipi nidificati e complessi. – drzaus

Problemi correlati