Search code examples
c#.netjson.netjsonconverter

How to set FloatParseHandling.Decimal for custom JsonConverter?


How can i set FloatParseHandling.Decimal for a custom JsonConverter?

we have a struct DecimalDbValue that internally only holds one decimal field that i want to be de/serialized for all its types.

It uses a magic number (decimal.MinValue) for indicating a "null" value. It was created before .net 2.0 having nullable value types!

This is a spripped down version of our struct::

    [Serializable]
    [JsonConverter(typeof(DecimalDbValueJsonConverter))]
    public struct DecimalDbValue : ISerializable
    {
        private readonly Decimal _decValue;

        public DecimalDbValue(
            decimal init)
        {
            _decValue = init;
        }

        [JsonConstructor]
        public DecimalDbValue(
            decimal? init)
        {
            if (init.HasValue)
                _decValue = init.Value;
            else
                _decValue = decimal.MinValue;
        }

        private DecimalDbValue(
            SerializationInfo objSerializationInfo,
            StreamingContext objStreamingContext)
        {
            _decValue = objSerializationInfo.GetDecimal("value");
        }

        public bool IsNotNull
        {
            get
            {
                return !IsNull;
            }
        }

        public bool IsNull
        {
            get
            {
                return _decValue.Equals(Decimal.MinValue);
            }
        }

        public Decimal Value
        {
            get
            {
                return _decValue;
            }
        }

        public void GetObjectData(
            SerializationInfo info,
            StreamingContext context)
        {
            info.AddValue("value", _decValue);
        }
}

I created a JsonConverter:

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

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var value = reader.Value == null ? (decimal?)null : Convert.ToDecimal(reader.Value);
            return new DecimalDbValue(value);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var dbValue = (DecimalDbValue)value;
            if (dbValue.IsNull)
                writer.WriteNull();
            else
                writer.WriteValue(dbValue.Value);
        }
    }

and set attribute [JsonConverter(typeof(DecimalDbValueJsonConverter))] on the DecimalDbValue struct

I have added a test:

        [Test]
        public void TestMaxDecimalDbValue()
        {
            var s = new DecimalDbValue(decimal.MaxValue);
            var json = JsonConvert.SerializeObject(s, Formatting.Indented);
            var x = JsonConvert.DeserializeObject<DecimalDbValue>(json);

            Assert.AreEqual(s, x);
        }

but it throws: System.OverflowException : Value was either too large or too small for a Decimal.

How can i set FloatParseHandling.Decimal for the JsonConverter? How to make it work for MaxValue as well? Is there any other way?

Actually i would like to have it serialize/deserialized exactly like an decimal? (nullable decimal)

Thanks


Solution

  • It seems to be impossible.

    Therefor i removed the JsonConverter completely.

    Set this attribute on the struct:

    [JsonObject(MemberSerialization.OptIn)]
    

    as well as this on the constructor:

     [JsonConstructor]
        public DecimalDbValue(
            decimal? init) //IMPORTANT: if you change the name of init - rename the JsonProperty below too - you might break all existing json out there thou!
    

    as well as this on the get property:

    [JsonProperty("init")]
        public Decimal Value
        {
            get
            {
                return _decValue;
            }
        }
    

    Unfortunately this bloats the json:

    {
        "init": 79228162514264337593543950335.0
    }