Serialize NaN values into JSON as nulls in JSON.NET

14,292

Solution 1

The author advises us to “Write a JsonConverter for float/double to make NaN safe if this is important for you,” so that’s what you can do:

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

and then use it:

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

Solution 2

Raphael Schweikerts solution with float support:

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

Solution 3

For the sake of future readers, if zeros are acceptable to you instead of nulls, it seems this issue has been addressed by Json.Net.

Serializing NaN and Infinity Floating Point Values

Json.NET no longer serializes NaN and positive and negative infinity floating point values as symbols, which is invalid JSON. With 5.0 the new default is to serialize those values as strings, e.g. "NaN" instead of NaN. There is no change to serializing normal floating point numbers.

A FloatFormatHandling setting has been added so you can control how NaN and infinity values are serialized.

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]
Share:
14,292

Related videos on Youtube

ACK_stoverflow
Author by

ACK_stoverflow

I like things.

Updated on June 04, 2022

Comments

  • ACK_stoverflow
    ACK_stoverflow over 1 year

    Most Json parsers don't serialize NaN, because in Javascript, NaN is not a constant. Json.Net, however, does serialize NaN values into NaN, which means it outputs invalid Json; attempting to deserialize this Json will fail with most parsers. (We're deserializing in WebKit.)

    We have hacked the Json.Net code to output null values when passed NaN, but this seems like a poor solution. Douglas Crockford (once) recommended using nulls in place of NaNs:

    http://www.json.org/json.ppt (Look at slide 16)

    Clearly this won't work in all cases, but it would be ok for our purposes. We'd just rather not have to modify the source code of Json.Net. Does anyone know how to use Json.Net to convert NaN inputs into null outputs?

  • David Rettenbacher
    David Rettenbacher almost 11 years
    Doesn't work for double AND float - <float-value> as double? is always null!
  • Raphael Schweikert
    Raphael Schweikert almost 11 years
    You’re right, it does not work for floats. I’m not exactly sure why (double?) value yields null when value is a float but (double?) (float?) value is ok. I’ve updated my answer with a working solution. Thanks!
  • Sergiy Belozorov
    Sergiy Belozorov almost 10 years
    If preserving the original behavior for normal values is important, one may also replace write.WriteValue(val) with if (value is float) writer.WriteValue((float)val); else writer.WriteValue((double)val). Otherwise, suddenly 3.14f may be serialized as 3.1400001049041748 instead of 3.14. This broke one of the unit-tests in my application.
  • David Rettenbacher
    David Rettenbacher almost 10 years
    Thanks for pointing this out but I couldn't accept your edit anymore (was already rejected!?)... I changed the answer according to your edit.