Search code examples
json.netdeserializationravendb

RavenDB 5.4 serializing and deserializing Decimal format


I have a requirement that decimal values retrieved from RavenDB have the exact same format they were stored as.

Example property value 33

The serialized json stored in DB have the value stored exactly as 33, which is as expected. But when deserializing I get value 33.0 in the Decimal property.

I have tried with a custom JsonConverter just to inspect what happens when deserializing

public class DecimalFormatConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(decimal);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return Convert.ToDecimal(reader.Value);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue((decimal)value);
    }
}

In ReadJson, when inspecting reader.Value it is 33.0 (should be 33). I cannot implement a rounding or custom formatting here, since if the value was stored as 33.0 or 33.00 it MUST keep the exact same format when loaded.

I have set

serializer.FloatParseHandling = Newtonsoft.Json.FloatParseHandling.Decimal;

although this is probably not relevant in this case.


Solution

  • Warning! The solution posted by @Ayende Rahien with the second comment, has a serious potential bug.

    If the code is run with different Cultures decimal formats (f.ex decimal separator . or , depending on Culture) it will mess up the decimals.

    The best solution I have found so far is:

    public class DecimalFormatConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(decimal) || objectType == typeof(decimal?);
        }
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.Value == null)
                return null;
    
            return Convert.ToDecimal(reader.Value, CultureInfo.InvariantCulture);
        }
            
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            writer.WriteValue(Convert.ToDecimal(value).ToString(CultureInfo.InvariantCulture));
        }
    }