1
votes

I have a JSON file with an array of objects, each containing a string value, grade, that I'd like to parse to decimal.

The string value contains a valid decimal about 99% percent of the time, but in that 1%, I'm getting values such as "grade":"<1" which is obviously not a valid decimal. The grade property is about 1 of 100 properties that can sometimes be set to "<1".

This of course throws the following error:

Newtonsoft.Json.JsonReaderException: 'Could not convert string to decimal'

Here is how I am currently deserializing my JSON:

public static Product FromJson(string json) => JsonConvert.DeserializeObject<Product>(json, Converter.Settings);

Is there anything I can do to handle cases where I'm getting those pesky "<1" values? Hopefully something that does the following: if attempting to deserialize a value to decimal, and if the value cannot be parsed to decimal, default to zero.

Any ideas if this is possible? I obviously don't want to have to update my table columns to switch all values from decimal to varchar, because that just sucks and is going to require decimal <-> varchar conversions every time someone wants to query my data.

1
Simple solution would be to use Try { deserialize code ... } Catch(Exception exc) { ...} and handle the exception as you see fit. In your case you said you want to set it to a default value, so do go ahead and do that in the Catch block. Have you tried this already? - jdewerth
you can use a custom JsonConverter for decimal property. - Mohammed Sajid

1 Answers

2
votes

You can solve this problem by making a custom JsonConverter to handle the decimals:

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Float || reader.TokenType == JsonToken.Integer)
        {
            return Convert.ToDecimal(reader.Value);
        }
        if (reader.TokenType == JsonToken.String && decimal.TryParse((string)reader.Value, out decimal d))
        {
            return d;
        }
        return 0.0m;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

To use the converter, just add an instance to the Converters collection in the JsonSerializerSettings that you are passing to JsonConvert.DeserializeObject<T>.

Settings.Converters.Add(new TolerantDecimalConverter());

Note: since you are using decimals, you should probably also set FloatParseHandling to Decimal if you are not already; the default is Double.

Settings.FloatParseHandling = FloatParseHandling.Decimal;

Working demo here: https://dotnetfiddle.net/I4n00o