Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a default value for JSON.net for properties with invalid values

Tags:

json

c#

json.net

I am using the Newtonsoft JSON library to deserialize a response from a web service. The problem is that some of the fields contain values which are not valid. For example, one field on one record contained a "T" for a field that is supposed to be numeric. What I would like to do is to have values for fields that are invalid be null, or some other default value. All of my properties are defined as nullable, so it is fine if they default to null.

Is there a way to do this? I have tried creating custom JsonConverters, but I would prefer to not have to define a JsonConverter for each type. If possible, I would like to, for all fields, set them null if the value for that property is not valid (like a "T" for a numeric type).

I have looked at the OnError event handler, but that seems to discard a whole record on error. I do not want to discard any records. I would just like for the value of invalid properties to be null.

Is this possible? I have looked a lot for answers and I did not find another question that was trying to do this, but please let me know if I overlooked an existing question.

Thank you for your help.

like image 957
ashipma Avatar asked Sep 25 '22 09:09

ashipma


2 Answers

You could make a JsonConverter for all nullable types that tries to deserialize the corresponding JSON value to the underlying type when non-null, and upon failure, returns null:

public class NullableTypeConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return Nullable.GetUnderlyingType(objectType) != null;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var underlyingType = Nullable.GetUnderlyingType(objectType);
        if (underlyingType == null)
            throw new JsonSerializationException("Invalid type " + objectType.ToString());
        var token = JToken.Load(reader);
        try
        {
            return token.ToObject(underlyingType, serializer);
        }
        catch (Exception ex)
        {
            // Log the exception somehow
            Debug.WriteLine(ex);
            return null;
        }
    }

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

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

Then use it when deserializing your classes.

var settings = new JsonSerializerSettings { Converters = new[] { new NullableTypeConverter() } };

The converter will still throw an exception (from the call to JToken.Load()) if the JSON itself is malformed and cannot be read. It only swallows the exception if the JSON value cannot be converted to the appropriate type after being loaded.

like image 56
dbc Avatar answered Sep 28 '22 04:09

dbc


Try setting the Error delegate on the Deserialize and handle the error you are looking for by setting args.ErrorContext.Handled = true. Check out the example code below:

public class TestClass1
{
    public string Id { get; set; }
    public int Height { get; set; }
}

public class TestClass2
{
    public string Id { get; set; }
    public string Height { get; set; }
}

[TestMethod]
public void Test()
{
    TestClass2 x = new TestClass2() { Id = "1", Height = "something" };
    string str = JsonConvert.SerializeObject(x);

    JsonConvert.DeserializeObject<TestClass1>(str, new JsonSerializerSettings()
        {
            Error = delegate(object sender, ErrorEventArgs args)
            {
                if (args.ErrorContext.Error.GetType().FullName == typeof(JsonReaderException).FullName)
                    args.ErrorContext.Handled = true;
            }
    });
}
like image 29
Jerrod Horton Avatar answered Sep 28 '22 05:09

Jerrod Horton