Search code examples
c#enumsjson.net.net-4.8

Populating enums without exception


In our C# application on .NET 4.8, we tried to use a PopulateObject for deserializing data returned from an API. This API is not under our control.

Now the API developer added a new enum value and when we try to map it in our own Enum it crashes with a message like

Error converting value 'x' to 'enum-y'

Now we COULD adjust our enum and go on, but that would only be a temporary fix until it happens again at another part of the code. We searched a little bit and found a specific solution. We added an 'Unknown' value to our enum and then added a converter like this:

public class AllowedMethodConverter : StringEnumConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.String)
        {
            if (Enum.TryParse(reader.Value.ToString(), out Method result)) // Here the Method has to be known
                return result;
            else
                return Method.Unknown;
        }

        return base.ReadJson(reader, objectType, existingValue, serializer);
    }
}

[JsonConverter(typeof(AllowedMethodConverter))]
public enum Method
{
    Unknown,
    Method1,
    Method2
}

This is used in a very simple API method where we get the string from the API and afterwards:

JsonConvert.PopulateObject(response.Content, userSettings);

That works fine so far, but this would require one StringEnumConverter per enum. With roughly 40 enums, it would be a little bit much to create 40 converters, just so I can ignore some values that do not exist.

Does anyone have a better idea how to handle this?


Solution

  • Instead of defining a custom converter for each enum, we can create a single, reusable JsonConverter that works with any enum dynamically.

    Step 1: Implement a Generic Enum Converter

    public class SafeEnumConverter<TEnum> : JsonConverter where TEnum : struct, Enum
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(TEnum);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.String)
            {
                string enumString = reader.Value.ToString();
    
                if (Enum.TryParse(enumString, out TEnum result)) // Try to parse normally
                    return result;
    
                return Enum.GetValues(typeof(TEnum)).Cast<TEnum>().First(); // Default to first value (e.g., Unknown)
            }
    
            return existingValue ?? Enum.GetValues(typeof(TEnum)).Cast<TEnum>().First();
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            writer.WriteValue(value.ToString());
        }
    }
    

    Step 2: Apply to Enums

    Now, instead of defining a separate converter per enum, just use the same generic converter for all:

    [JsonConverter(typeof(SafeEnumConverter<Method>))]
    public enum Method
    {
        Unknown,
        Method1,
        Method2
    }
    
    [JsonConverter(typeof(SafeEnumConverter<AnotherEnum>))]
    public enum AnotherEnum
    {
        Unknown,
        Value1,
        Value2
    }
    

    Step 3: Use It in Your Code

    Now, when calling JsonConvert.PopulateObject(), the converter will automatically handle unknown values and default to Unknown:

    JsonConvert.PopulateObject(response.Content, userSettings);
    

    Why This Works

    Single Converter for All Enums – No need to create multiple converters
    Prevents Crashes – Unrecognized values are gracefully handled
    Scalable – Works with any enum
    Minimal Code Changes