[CSHARP-1718] Cannot deserialize floats from json Created: 21/Jul/16  Updated: 25/Jul/16  Resolved: 25/Jul/16

Status: Closed
Project: C# Driver
Component/s: BSON
Affects Version/s: 2.2.4
Fix Version/s: None

Type: Bug Priority: Critical - P2
Reporter: Toshko Andreev [X] Assignee: Robert Stam
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Ubuntu Server 14.04 LTS x64
Windows 10 Pro x64


Backwards Compatibility: Fully Compatible

 Description   

If you have a RootClass with a SubClass as a child property and it has two float properties then BsonSerializer.Deserialize<TheClass>(json); will throw an error saying that it cannot deserialize the second float. See this gist for re-pro code:

https://gist.github.com/Ravenheart/0f4f47bbe14737cb01998cf86bf0396d

This error occurs in ALL versions of the C# driver, including the legacy one.



 Comments   
Comment by Robert Stam [ 25/Jul/16 ]

You're welcome. Glad you got it working for you.

Comment by Toshko Andreev [X] [ 25/Jul/16 ]

Thank you Robert for the detailed explanation and support. I have corrected my code with the attribute fix.

Comment by Robert Stam [ 22/Jul/16 ]

It fails on the second property because 2.9 cannot be exactly represented as a binary floating point number, and when the closest available double value is converted to a float there is loss of precision (recall that floats only use 64 bits and doubles use 128 bits).

You can make it fail on the first property by just swapping the values (0.5 and 2.9).

It's not true that all values above 0.5 fail. You can try 0.75 for example. It only fails when there is a loss of precision when converting from a double to a float (which typically will be the case when the fractional decimal value does not have an exact representation as a binary double).

It's also not true that anything below 0.5 works fine. Try 0.3 for example.

If you can use double instead of float in your class this problem disappears. Otherwise you will have to decide whether you are willing to lose precision when converting doubles to floats and if so tell the driver that by annotating your class.

Comment by Toshko Andreev [X] [ 22/Jul/16 ]

Why does it throw the error on the second property then, and why does it only happen if the value is above 0.5 (anything below that and it works fine).

Comment by Robert Stam [ 21/Jul/16 ]

This error is happening because BSON only has double values, not floats. The driver serializes floats as doubles, but when converting doubles read from the database back to floats a loss of precision can occur. When the double value cannot exactly be represented as a float the driver throws an exception.

There are two things you could do to get this code working. The easiest is to just use doubles instead of floats:

public class SubClass
{
    public double Height { get; set; }
    public double Weight { get; set; }
}

The other is to annotate the class to tell the driver that you are OK with losing precision during deserialization:

public class SubClass
{
    [BsonRepresentation(BsonType.Double, AllowTruncation = true)]
    public float Height { get; set; }
    [BsonRepresentation(BsonType.Double, AllowTruncation = true)]
    public float Weight { get; set; }
}

Just be aware that in the second case you are losing precision, and if you read a document from the database and write it back the value written back to the database will be slightly different (due to rounding errors when converting from double to float and back to double).

Generated at Wed Feb 07 21:40:29 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.