75

I have an object with predefined data structure:

public class A
{
    public string Id {get;set;}
    public bool? Enabled {get;set;}
    public int? Age {get;set;}
}

and JSON is supposed to be

{ "Id": "123", "Enabled": true, "Age": 23 }

I want to handle JSON error in positive way, and whenever server returns unexpected values for defined data-types I want it to be ignore and default value is set (null).

Right now when JSON is partially invalid I'm getting JSON reader exception:

{ "Id": "123", "Enabled": "NotABoolValue", "Age": 23 }

And I don't get any object at all. What I want is to get an object:

new A() { Id = "123", Enabled = null, Age = 23 }

and parsing warning if possible. Is it possible to accomplish with JSON.NET?

Mando
  • 11,414
  • 17
  • 86
  • 167

3 Answers3

126

To be able to handle deserialization errors, use the following code:

var a = JsonConvert.DeserializeObject<A>("-- JSON STRING --", new JsonSerializerSettings
    {
        Error = HandleDeserializationError
    });

where HandleDeserializationError is the following method:

public void HandleDeserializationError(object sender, ErrorEventArgs errorArgs)
{
    var currentError = errorArgs.ErrorContext.Error.Message;
    errorArgs.ErrorContext.Handled = true;
}

The HandleDeserializationError will be called as many times as there are errors in the json string. The properties that are causing the error will not be initialized.

Ilija Dimov
  • 5,221
  • 7
  • 35
  • 42
  • 39
    Note that ErrorEventArgs is Newtonsoft.Json.Serialization.ErrorEventArgs and not System.IO.ErrorEventArgs. – Jamie Ide Mar 08 '16 at 18:41
  • 3
    How do you ignore a single faulty element of a collection? Using this method, it seems like Json.NET ignores the whole collection instead of just the faulty element. – Zero3 Jun 05 '17 at 18:26
  • 2
    In my experience JSON.NET will ignore the whole collection only if the json string is not well formed and it will ignore the faulty property of a single element if the json string is well formed but there is a type mismatch. (for example you are trying to deserialize a string to an integer property) – Ilija Dimov Jun 06 '17 at 06:59
  • 2
    @IlijaDimov Interesting. In my case, a property is missing somewhere in the subtree of the element. But instead of throwing an error at that location, Json.NET appears to start the error bubbling at the parent collection, and if I do `Handled = true` there, the whole collection gets set to null :/. – Zero3 Jun 06 '17 at 07:58
  • @Zero3 that's my experience as well. I have large json strings with heavy nesting. The failed property in my case is a datetime conversion. Since it's nullable, I'd like to set it to null but let the rest of the item/object it's in be parsed. I want to avoid a custom converter (https://stackoverflow.com/a/21542099/2779990) due to the size and complexity of my json. – Stinky Towel Jun 09 '17 at 17:35
  • 1
    @JamieIde You saved my day buddy, thanks a bunch! I wrote the code exactly from the Newtonsoft documentation, but it kept saying something like _Cannot convert anonymous method to 'delegate' type EventHandler because the parameter type doesn't match the delegate parameter type_. Then I prefixed `ErrorEventArgs` with its namespace, and then VS recognized what's what. ;-) – Sнаđошƒаӽ Oct 15 '17 at 15:05
  • This causes a massive memory leak, because exceptions are still thrown, they just don't end the serialization. – Vladimir Kocjancic Oct 12 '18 at 09:20
36

Same thing as Ilija's solution, but a oneliner for the lazy/on a rush.

var settings = new JsonSerializerSettings { Error = (se, ev) => { ev.ErrorContext.Handled = true; } };
JsonConvert.DeserializeObject<YourType>(yourJsonStringVariable, settings);

(Thanks to commenter for shortening it even more)

Gaspa79
  • 5,488
  • 4
  • 40
  • 63
5

There is another way. for example, if you are using a nuget package which uses newton json and does deseralization and seralization for you. You may have this problem if the package is not handling errors. then you cant use the solution above. you need to handle in object level. here becomes OnErrorAttribute useful. So below code will catch any error for any property, you can even modify within the OnError function and assign default values

public class PersonError
{
  private List<string> _roles;

  public string Name { get; set; }
  public int Age { get; set; }

  public List<string> Roles
  {
    get
    {
        if (_roles == null)
        {
            throw new Exception("Roles not loaded!");
        }

        return _roles;
    }
    set { _roles = value; }
  }

  public string Title { get; set; }

  [OnError]
  internal void OnError(StreamingContext context, ErrorContext errorContext)
  {
    errorContext.Handled = true;
  }
}

see https://www.newtonsoft.com/json/help/html/SerializationErrorHandling.htm

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
Emil
  • 6,411
  • 7
  • 62
  • 112