2011-10-18 10 views
7

La maggior parte dei parser Json non serializza NaN, perché in Javascript, NaN non è una costante. Json.Net, tuttavia, serializza i valori NaN in NaN, il che significa che emette Json non valido; tentare di deserializzare questo Json fallirà con la maggior parte dei parser. (Stiamo deserializzando in WebKit.)Serializzare i valori NaN in JSON come valori nulli in JSON.NET

Abbiamo hackerato il codice Json.Net per emettere valori nulli quando è passato NaN, ma questa sembra una soluzione scadente. Douglas Crockford (una volta) consiglia di utilizzare null al posto di Nans:

http://www.json.org/json.ppt (Guarda cursore 16)

Chiaramente questo non funziona in tutti i casi, ma sarebbe bene per i nostri scopi. Preferiamo semplicemente non modificare il codice sorgente di Json.Net. Qualcuno sa come usare Json.Net per convertire gli input NaN in output nulli?

risposta

8

L'autore advises us a “Scrivi una JsonConverter per float/double di fare NaN sicuro se questo è importante per voi”, ed è quello che si può fare:

class LawAbidingFloatConverter : JsonConverter { 
    public override bool CanRead 
    { 
     get 
     { 
      return false; 
     } 
    } 
    public override bool CanWrite 
    { 
     get 
     { 
      return true; 
     } 
    } 
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     var val = value as double? ?? (double?) (value as float?); 
     if (val == null || Double.IsNaN((double)val) || Double.IsInfinity((double)val)) 
     { 
      writer.WriteNull(); 
      return; 
     } 
     writer.WriteValue((double)val); 
    } 
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(double) || objectType == typeof(float); 
    } 
} 

e poi usarlo:

var settings = new JsonSerializerSettings(); 
var floatConverter = new LawAbidingFloatConverter(); 
settings.Converters.Add(floatConverter); 
var myConverter = new JsonNetSerializer(settings); 
soluzione
+2

non funziona per 'double' ** e **' float' - ' come doppio' è ** sempre ** 'null'?! –

+1

Hai ragione, non funziona per 'float's. Non sono esattamente sicuro del perché '(double?) Value' restituisce' null' quando value è un float ma '(double?) (Float?) Value' è ok. Ho aggiornato la mia risposta con una soluzione funzionante. Grazie! –

4

Raphael Schweikerts con float supporto:

public class StandardFloatConverter : JsonConverter 
{ 
    public override bool CanRead 
    { 
     get 
     { 
      return false; 
     } 
    } 
    public override bool CanWrite 
    { 
     get 
     { 
      return true; 
     } 
    } 
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     if (value == null) 
     { 
      writer.WriteNull(); 
      return; 
     } 

     var val = Convert.ToDouble(value); 
     if(Double.IsNaN(val) || Double.IsInfinity(val)) 
     { 
      writer.WriteNull(); 
      return; 
     } 
     // Preserve the type, otherwise values such as 3.14f may suddenly be 
     // printed as 3.1400001049041748. 
     if (value is float) 
      writer.WriteValue((float)value); 
     else 
      writer.WriteValue((double)value); 
    } 
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(double) || objectType == typeof(float); 
    } 
} 
+1

Se è importante preservare il comportamento originale per i valori normali, si può anche sostituire 'write.WriteValue (val)' con 'if (value is float) writer.WriteValue ((float) val); else writer.WriteValue ((double) val) '. Altrimenti, improvvisamente '3.14f' può essere serializzato come' 3.1400001049041748' invece di '3.14'. Questo ha rotto uno dei test unitari nella mia applicazione. –

+0

Grazie per la segnalazione, ma non ho più potuto accettare la tua modifica (era già stata rifiutata!?) ... Ho cambiato la risposta in base alla tua modifica. –

2

Per motivi di futuri lettori, se gli zeri sono accettabili per voi anziché i valori nulli, sembra che questo problema sia stato risolto da Json.Net.

serializzazione NaN e Infinito Floating Point Valori

Json.NET non serializza infinito positivo e negativo valori in virgola mobile e NaN come simboli, che è JSON valido. Con 5.0 il nuovo valore predefinito di consiste nel serializzare tali valori come stringhe, ad es. "NaN" anziché NaN. Non vi sono modifiche alla serializzazione dei normali numeri di riferimento mobili .

Un'impostazione FloatFormatHandling è stata aggiunta in modo da poter controllare come i valori di NaN e infinito sono serializzati.

string json;  
IList<double> d = new List<double> {1.1, double.NaN, double.PositiveInfinity}; 

json = JsonConvert.SerializeObject(d); 

// [1.1,"NaN","Infinity"] 

json = JsonConvert.SerializeObject(d, new JsonSerializerSettings {FloatFormatHandling = FloatFormatHandling.Symbol}); 

// [1.1,NaN,Infinity] 

json = JsonConvert.SerializeObject(d, new JsonSerializerSettings {FloatFormatHandling = FloatFormatHandling.DefaultValue}); 

// [1.1,0.0,0.0] 
Problemi correlati