2011-10-18 12 views
7

La mayoría de los analizadores Json no serializan NaN, porque en Javascript, NaN no es una constante. Json.Net, sin embargo, serializa los valores NaN en NaN, lo que significa que genera Json no válido; intentar deserializar a este Json fracasará con la mayoría de los analizadores sintácticos. (Estamos deserializando en WebKit.)Serializar valores NaN en JSON como nulos en JSON.NET

Hemos pirateado el código Json.Net para generar valores nulos cuando se pasa NaN, pero esto parece una solución pobre. Douglas Crockford (una vez) recomienda el uso de valores nulos en lugar de NaNs:

http://www.json.org/json.ppt (Mira corredera 16)

Es evidente que esto no funcionará en todos los casos, pero que estaría bien para nuestros propósitos. Preferiríamos no tener que modificar el código fuente de Json.Net. ¿Alguien sabe cómo usar Json.Net para convertir las entradas de NaN en salidas nulas?

Respuesta

8

El autor advises us a “Escribir un JsonConverter de flotador/doble para compensar NaN seguro si esto es importante para usted,” y eso es lo que puede hacer:

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); 
    } 
} 

y luego usarlo:

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

no funciona para 'double' ** y **' float' - '' doble como es ** ** 'siempre null'?! –

+1

Tienes razón, no funciona para 'float's. No estoy exactamente seguro de por qué '(double?) Value' produce' null' cuando el valor es un flotante, pero '(double?) (Float?) Value' está bien. He actualizado mi respuesta con una solución de trabajo. ¡Gracias! –

4

Raphael Schweikerts con float apoyo:

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

Si es importante preservar el comportamiento original para los valores normales, también se puede reemplazar 'write.WriteValue (val)' con 'if (value is float) writer.WriteValue ((float) val); else writer.WriteValue ((double) val) '. De lo contrario, de repente '3.14f' puede ser serializado como' 3.1400001049041748' en lugar de '3.14'. Esto rompió una de las pruebas unitarias en mi aplicación. –

+0

Gracias por señalar esto, pero no pude aceptar más tu edición (¡ya fue rechazada!?) ... Cambié la respuesta según tu edición. –

2

Por el bien de los lectores futuros, si le son aceptables los ceros en lugar de los nulos, parece que este problema ha sido tratado por Json.Net.

números de serie a NaN y Infinity Floating Point Valores

Json.NET ya no serializa los valores de punto flotante infinito positivo y negativo NaN y como símbolos, que es JSON válido. Con 5.0, el nuevo valor predeterminado es serializar esos valores como cadenas, p. "NaN" en lugar de NaN. No hay cambios en la serialización de los números de punto flotantes normales .

Se ha agregado una configuración de FloatFormatHandling para que pueda controlar cómo se serializan NaN y los valores de infinito.

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] 
Cuestiones relacionadas