Newtonsoft.json: Enum Converters - Unknown Value Handling

Created on 28 Jun 2017  ·  4Comments  ·  Source: JamesNK/Newtonsoft.Json

Hi Newtonsoft,
I'm not sure if this is a bug or a feature request, but here goes:
I am building an app that connects to a third-party service. In lots of places, the service returns string constants that can better be represented as Enums. New values aren't added often, but they are added on occasion, and when that happens, I don't want my code to start crashing. I want to just be able to deserialize the unknown enum as some other value.

I've tried using DefaultValue and other methods to no avail. StackOverflow seems to indicate that you have to roll your own converter to make this happen but that seems like overkill.

I've noticed that JsonSerializerSettings has a lot of properties that control how different types are deserialized but none have anything to do with Enums.

Perhaps I'm doing something wrong, but if not, I propose adding the following member:
JsonSerializerSettings.UnknownEnumValueHandling with the options of:
ThrowException - The current default behavior - an exception is thrown.
Ignore - the enum value is ignored.
UseDefault - The enum's default value is used. This should pick up the value from a DefaultValue attribute or alternately, perhaps a new UnknownValue attribute that can be applied to members or enums.

It would also be great if it could convert Enums that have underscores in the values. For example,

in_progress (string) -> Status.InProgress (enum)

Below is the code that gives a small example of the problem.

Source/destination types

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.ComponentModel;

namespace ConsoleApp7 {
    class Program {
        static void Main(string[] args) {
            var Contents = new[]{

                @"{""Status"": ""Open""}"
                , @"{""Status"": ""open""}"
                , @"{""Status"": ""closed""}"
                , @"{""Status"": ""Pizza""}"
            };


            var Settings = new Newtonsoft.Json.JsonSerializerSettings();
            Settings.DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Populate;
            Settings.DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.

            foreach(var item in Contents) {
                try {
                    var Matter = Newtonsoft.Json.JsonConvert.DeserializeObject<Matter>(item, Settings);
                } catch (Exception ex) {

                }
            }


        }
    }

    [DefaultValue(MatterStatus.Unknown)]
    public enum MatterStatus {
        Unknown,
        Open,
        Closed,
        Pending,
    }

    public class Matter {
        [DefaultValue(MatterStatus.Unknown)]
        public MatterStatus Status { get; set; }
    }

}

Most helpful comment

Any updates? It's quite useful for me. If being able, I will implement this while solving my problem here at #1597

All 4 comments

I ended up hitting a very similar scenario recently. I have a web service that returns a json containing an array of strings, and each of those strings corresponds to an enum value. However, the client program that consumes that API endpoint only needs to know a small subset of those strings, which are conveniently represented as enums in my code (using the [EnumMember(Value = "actual_string_representation")] to define the string representation of the enum). The built in StringEnumConverter throws an exception whenever it hits one of the values that aren't defined, which isn't ideal.

My current solution is to use a class called TolerantEnumConverter, and use it to deserialize the array with the attribute [JsonProperty(PropertyName = "my_enums", ItemConverterType = typeof(TolerantEnumConverter))]

The code for the TolerantEnumConverter can be found here:

https://gist.github.com/gubenkoved/999eb73e227b7063a67a50401578c3a7

I agree with you that using this is only a stopgap, it's pretty overkill since it mostly just uses code from StringEnumConverter anyway.

In order for this functionality to be built into Json.Net, I think StringEnumConverter would need to be modified in order to accept the "UnknownEnumValueHandling" parameter and select the default value. The TolerantEnumConverter above contains some pretty good code which could be spliced in - mainly in the way it selects the default value:

If the enum is nullable, the then null is used as the default.

If any of the enums are named "Unknown", they are used as the default.

Otherwise, the first enum is used as the default. This should be modified to also take into account and prioritise the [DefaultValue] attribute.

Another thing: How would the UnknownEnumValueHandling.Ignore setting be interpreted? I don't think it's actually possible to "Ignore" an enum, since it has so be set to something during deserialization.

It would also be great if it could convert Enums that have underscores in the values. For example, in_progress (string) -> Status.InProgress (enum)

If I understand what you're trying to do correctly, this is already possible by putting the [EnumMember] attribute above where the enum is defined, like so:

enum SomeEnum {
    [EnumMember(Value = "some_body")]
    SomeBody,
    [EnumMember(Value = "once_told_me")]
    OnceToldMe,
    [EnumMember(Value = "that_enums")]
    ThatEnums,
    [EnumMember(Value = "were_gonna_role_me")]
    WereGonnaRollMe
}

The StringEnumConverter will detected the [EnumMember] attribute and look for the string value defined there, instead of using the default string representation of the enum.

Any updates on this issue? Is it even considered? Still have to inherit from StringEnumConverter to support DefaultValue.

Any updates? It's quite useful for me. If being able, I will implement this while solving my problem here at #1597

Was this page helpful?
0 / 5 - 0 ratings