C# Enum deserialization with Json.Net: Error converting value to type

16,801

Solution 1

Let's say we have the following json string:

[
    {
        "Name": "abc",
        "MyEnumValue": "Type1"
    },
    {
        "Name": "abcd",
        "MyEnumValue": "Type2"
    },
    {
        "Name": "abcde",
        "MyEnumValue": "Type3"
    }    ,
    {
        "Name": "abcdef",
        "MyEnumValue": "Type4"
    }
]

and the following class and enum:

public class MyClass
{
    public string Name { get; set; }

    public MyEnum MyEnumValue { get; set; }
}

public enum MyEnum
{
    Type1,
    Type2,
    Type3
}

As it can be noticed, the json string array contains item (the last one), that cannot be correctly mapped to the MyEnum. To avoid deserialization errors you can use the following code snippet:

static void Main(string[] args)
{         
    var serializationSettings = new JsonSerializerSettings
    {
        Error = HandleDeserializationError
    };

    var lst = JsonConvert.DeserializeObject<List<MyClass>>(jsonStr, serializationSettings);
}

public static void HandleDeserializationError(object sender, ErrorEventArgs errorArgs)
{
    errorArgs.ErrorContext.Handled = true;
    var currentObj = errorArgs.CurrentObject as MyClass;

    if (currentObj == null) return;
    currentObj.MyEnumValue = MyEnum.Type2;            
}

where the jsonStr variable is the posted json string above. In the above code sample, if MyEnumValue cannot be correctly interpreted, it is set to a default value of Type2.

Example: https://dotnetfiddle.net/WKd2Lt

Solution 2

The only way I see it, you should write your own converter. But half of work is already done in class StringEnumConverter. We can override only ReadJson method

class Program
{
    static void Main(string[] args)
    {
        const string json = @"{
                'Name': 'abc',
                'Type':'Type4'
            }";

        // uncomment this if you want to use default value other then default enum first value
        //var settings = new JsonSerializerSettings();
        //settings.Converters.Add(new FooTypeEnumConverter { DefaultValue = FooType.Type3 });

        //var x = JsonConvert.DeserializeObject<Foo>(json, settings);

        var x = JsonConvert.DeserializeObject<Foo>(json);
    }
}

public class Foo
{
    public string Name { get; set; }

    public FooType Type { get; set; }
}

public enum FooType
{
    Type1,
    Type2,
    Type3
}

public class FooTypeEnumConverter : StringEnumConverter
{
    public FooType DefaultValue { get; set; }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            return base.ReadJson(reader, objectType, existingValue, serializer);
        }
        catch (JsonSerializationException)
        {
            return DefaultValue;
        }
    }
}

Solution 3

An alternative, if you don't want to create a custom converter, is to map it to a private string field in your DTO and then use Enum.TryParse in that field's property getter:

public class MyClass
{
    [JsonProperty("MyEnumValue")]
    private string myEnumValue;

    public string Name { get; set; }

    [JsonIgnore]
    public MyEnum MyEnumValue 
    { 
        get
        {
            MyEnum outputValue = MyEnum.Default;
            Enum.TryParse(myEnumValue, out outputValue);
            return outputValue;
        }
    }
}
Share:
16,801

Related videos on Youtube

Mina Wissa
Author by

Mina Wissa

Developer with experience in developing mobile applications for Android, iOS and Windows with native SDKs and cross platform frameworks. www.linkedin.com/in/mina-wissa

Updated on June 06, 2022

Comments

  • Mina Wissa
    Mina Wissa almost 2 years

    I'm using Json.NET to serialize/deserialize some JSON APIs.

    The API response have some integer values that map to an Enum defined in the application.

    The enum is like this:

    public enum MyEnum
    {
        Type1,
        Type2,
        Type3
    }
    

    and the JSON API response has the following:

    {
        "Name": "abc",
        "MyEnumValue":"Type1"
    }
    

    sometimes the API returns a value for the MyEnumValue field that's not defined in my enum, like this:

    {
        "Name": "abc",
        "MyEnumValue":"Type4"
    }
    

    That throws an exception:

    Error converting value "Type4" to type 'MyEnum'

    Is there a way to handle this error by assigning a default value or something to avoid the application crash?

    • 3dd
      3dd almost 9 years
      Have you tried specifying the DefaultValue for the property newtonsoft.com/json/help/html/…
    • Mina Wissa
      Mina Wissa almost 9 years
      Thanks, I tried but still the exception is thrown
  • Mina Wissa
    Mina Wissa almost 9 years
    Thanks, this is a nice solution
  • Mina Wissa
    Mina Wissa almost 9 years
    Thanks, that can be another alternative solution
  • Mike Taverne
    Mike Taverne about 8 years
    Yikes, how is assigning a default value a solution? Maybe Type5 or Type6 or TypeX cannot be equated to Type2? The point is the client cannot anticipate what the server might return in future, so assuming you can assign a default value is dangerous.
  • Nikunj Patel
    Nikunj Patel almost 4 years
    Can you also look into this problem, json with Enum DisplayName or EnumMember Value