1

I'm using YAML to communicate between C# GUI and server side Java, which is working fine in general. However, if I pass a field that is a Double and the value is Double.NaN on Java side the Yaml passes as ".NaN", and when I come to deserialize on the C# side a 'System.FormatException' is thrown as C# expects the string "NaN" [not ".NaN"].

Does anyone know if there is a way to intercept the deserializer, or add formatting so that on the C# side ".NaN" can be parsed in a double?

(One workaround I can think of is changing all NaN's to a special value before serliazing to YAML, and then on C# recognizing the special value and converting back to NaN, but this seems like a big hack.)

Chill
  • 77
  • 11
  • I found this [link](http://stackoverflow.com/questions/15301101/how-to-change-nan-string-representation-in-c), and noticed that after applying the following worked: Double.Parse(".NaN"), however it seems that YamlDotNet creates it's own NumberFormatInfo [link](https://github.com/aaubry/YamlDotNet/blob/master/YamlDotNet/Serialization/NodeDeserializers/ScalarNodeDeserializer.cs), so I guess the real question is how to override the NumberFormatInfo used in YamlDotNet. Any ideas? – Chill Sep 23 '15 at 14:05

1 Answers1

0

It seems that this is a bug in the way YamlDotNet handles floats. Until it is fixed, you can work around it by registering a custom node INodeDeserializer that will handle these special cases.

Here is a quick-and-dirty implementation of such a deserializer:

public class FloatNodeDeserializer : INodeDeserializer
{
    private static readonly Dictionary<Tuple<Type, string>, object> SpecialFloats =
        new Dictionary<Tuple<Type, string>, object>
    {
        { Tuple.Create(typeof(float), ".nan"), float.NaN },
        { Tuple.Create(typeof(float), ".inf"), float.PositiveInfinity },
        { Tuple.Create(typeof(float), "-.inf"), float.NegativeInfinity },

        { Tuple.Create(typeof(double), ".nan"), double.NaN },
        { Tuple.Create(typeof(double), ".inf"), double.PositiveInfinity },
        { Tuple.Create(typeof(double), "-.inf"), double.NegativeInfinity },
    };

    bool INodeDeserializer.Deserialize(
        EventReader reader,
        Type expectedType,
        Func<EventReader, Type, object> nestedObjectDeserializer,
        out object value
    ) {
        var scalar = reader.Peek<Scalar>();
        if (scalar == null) {
            value = null;
            return false;
        }

        var found = SpecialFloats.TryGetValue(
            Tuple.Create(expectedType, scalar.Value),
            out value);

        if(found) {
            reader.Allow<Scalar>();
        }
        return found;
    }
}

The way to register it is:

var deserializer = new Deserializer();
deserializer.NodeDeserializers.Insert(0, new FloatNodeDeserializer());

See a fully working fiddle here

Antoine Aubry
  • 12,203
  • 10
  • 45
  • 74
  • Fantastic! Thanks Antoine. FYI, I had to make very slight change to FloatNodeDeserializer. When getting from dictionary I put scalar.Value.ToLower() as the value was .NaN, hence .nan was not found. Appreciate your quick response! – Chill Sep 24 '15 at 14:35