5

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.

5
  • Possible alternative is just changing the definition in your classes to object then doing the checking post parsing. Or possibly something like this Let me know if I'm mistaken in your intent. Commented Feb 11, 2016 at 20:35
  • @Gabe Changing the types of the C# properties to object would probably work, but if possible I would prefer to not do that because I would like all of the properties in the C# class to be the correct type. What I am trying to do is to to take a JSON response like this: {"count": "T"} (where count is supposed to be defined as an integer) and to use the value null for the Count property in the C# class. In the actual response, there are a lot more fields than just one, so I do not want to discard the whole record. I just want a default value, like null, for each property. Thanks for your help. Commented Feb 11, 2016 at 20:43
  • dbc's custom converter is the best option - you will still need to null check. You can do default values and annotations but neither of those will stop the issues produced when deserializing. Did a few mockups and the converter is by far the most usable option. I am curious however - why store empty/null values as a string? Are you using it for generics (probably not given you want to null it)? Do you have access to the way the data is being delivered / stored? Database limitations? Fixing this on the 'sender' end would resolve all your issues. Commented Feb 11, 2016 at 21:53
  • 1
    @Gabe I agree that correcting the data is the best option, but I am using the Weather Undergroup API to get historical weather information and at least one record (I forget which one) had a value of "T" for a numeric value. I still want to pull back that record and load it into a database because there are still other properties that are valid. So I am trying to just store a null value for the properties which are invalid. But I do agree that it is best to correct the data if possible. Commented Feb 11, 2016 at 22:08
  • Ahh I see - Glad you got it fixed with annotations. However the converter that Adc proposed is something good to have in your arsonal. Good Luck with your project :) Commented Feb 11, 2016 at 22:10

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

2 Comments

By far the best option after testing.
Thank you for your help. This solves the question I was asking about. I ended up having to apply [JsonConverter(typeof(NullableTypeConverter) as an attribute for each property instead of setting it in the settings object because there were two properties which were actually not nullable that I did not mention before. But it works. Thank you again for your help.
2

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;
            }
    });
}

1 Comment

Thank you for your help with this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.