39

I have an enumeration defined with C#, where I'm storing it's values as characters, like this:

public enum CardType
{
    Artist = 'A',
    Contemporary = 'C',
    Historical = 'H',
    Musician = 'M',
    Sports = 'S',
    Writer = 'W'
}

I'm attempting to deserialize using JSON.NET, but the incoming JSON was written using the CHAR value (string) instead of the int value of the enumeration, like this:

[{"CardType","A"},{"CardType", "C"}]

Is it possible to define some kind of converter that will allow me to manually parse the char to the enum value?

I tried creating a JsonConverter, but am not sure how to do it, while applying it only to this property and not the whole parsed object. here's what I tried:

public class EnumerationConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }

        int value = serializer.Deserialize<int>(reader);
        return (CardType)value;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsSubclassOf(typeof(string));
    }
}

The logic might be wrong and I can fix that but the problem is ReadJson() isn't being called at all.

CanConvert is, but it appears to be called for every property, not just the one property I defined it for:

public class Card
{
            private CardType type;
        [JsonConverter(typeof(EnumerationConverter))]
        public CardType Type
        {
            get { return type; }
            set { type = value; }
        }
}

I'm sure I've done this incorrectly but am having trouble finding documentation on how to do this for a single field...

What am I missing?

SelAromDotNet
  • 4,715
  • 5
  • 37
  • 59
  • Can you post a sample class which has the `CardType` property where the deserilization fails? – nemesv Aug 31 '13 at 18:28
  • can u post the complete enum. I am sure you can not declare enum as you have defined above – dharam Aug 31 '13 at 18:29
  • the class being deserialized into just has a CardType property to receive the deserialized value, there's nothing in there other than the declaration. As for the enum value, that is indeed the full definition of the ENUM. the Card class has a property of type CardType for storing the enumerated type value. – SelAromDotNet Aug 31 '13 at 18:31
  • I am getting a compilation error for this enum... is this in java?? – dharam Aug 31 '13 at 18:32
  • my apologies, this is C#, I should have specified in description will update, sorry about that – SelAromDotNet Aug 31 '13 at 18:33
  • Ok, I added the tag C#. Well I am sure there is no built in deserializer. You can follow this post to write your custom deserializer for enums http://stackoverflow.com/questions/8790389/jackson-deserialize-one-base-enums – dharam Aug 31 '13 at 18:36
  • I cannot repro your issue... can you post your serilzation and deseriazation code? Because by default json.net generates `[{"CardType","65"},{"CardType", "67"}]` – nemesv Aug 31 '13 at 18:36
  • you are correct, digging deeper it appears that when the JSON is created, the type is a string, not an enum. I've updated the description to show the newly defined problem – SelAromDotNet Aug 31 '13 at 18:48
  • **Json**StringEnumConverter if you are using System.Text.Json.Serialization instead of Newtonsoft – Anderson Martins Jan 08 '22 at 07:10

4 Answers4

56

You don't necessary need a custom JsonConverter you can use the built in StringEnumConverter with the combination of the EnumMemberAttribute (from the System.Runtime.Serialization assembly).

Without the EnumMemberAttribute it uses the enum names so Artist, Contemporary, etc so you need to change the names with it to your A,C, etc value.

But it is not the nicest solution because you have to repeat your values two times, but it works:

[JsonConverter(typeof(StringEnumConverter))]
public enum CardType
{
    [EnumMember(Value = "A")]
    Artist = 'A',
    [EnumMember(Value = "C")]
    Contemporary = 'C',
    [EnumMember(Value = "H")]
    Historical = 'H',
    [EnumMember(Value = "M")]
    Musician = 'M',
    [EnumMember(Value = "S")]
    Sports = 'S',
    [EnumMember(Value = "W")]
    Writer = 'W'
}
nemesv
  • 138,284
  • 16
  • 416
  • 359
  • 1
    wow this looks like an ideal solution, and it definitely works great, thank you! – SelAromDotNet Aug 31 '13 at 19:07
  • 5
    How to deserialize the above one? i am having issues in deserializing this. Ex: Service has this enum: [EnumMember(Value = "Contemp")] Contemporary = 'C', UI code has this enum: [EnumMember(Value = "Contemp")] Contemporary = 'C' So, while deserializing, it is not able to find "Contemp".. Is there any way to do that? or just interchange the Enum and Value to, [EnumMember(Value = "ontemporary ")] Contemp = 'C', – Priya Payyavula Oct 20 '16 at 13:42
8

This code works perfectly:

CardType[] array = { CardType.Artist, CardType.Contemporary };
string s = JsonConvert.SerializeObject(array);
var array2 = JsonConvert.DeserializeObject<CardType[]>(s);

Update:
What about out-of-box StringEnumConverter:

[JsonConverter(typeof(StringEnumConverter))]
public CardType Type { get; set; }
Vladimir
  • 7,345
  • 4
  • 34
  • 39
  • yep, I'm sorry I was wrong, the JSON isn't being generated by a serialization, it's a custom JSON string, that uses the char value instead of the int. please see the updated description and thank you for your help! – SelAromDotNet Aug 31 '13 at 18:49
2

You can just add SerializerSettings.Converters.Add(new StringEnumConverter());

to your BrowserJsonFormatter class

public class BrowserJsonFormatter : JsonMediaTypeFormatter
{
    public BrowserJsonFormatter()
    {
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
        SerializerSettings.Formatting = Formatting.Indented;
        SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
        SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        SerializerSettings.Converters.Add(new EmptyToNullConverter());
        SerializerSettings.Converters.Add(new StringEnumConverter());
        //SerializerSettings.DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate;
    }

    public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
    {
        base.SetDefaultContentHeaders(type, headers, mediaType);
        headers.ContentType = new MediaTypeHeaderValue("application/json");
    }
}
0

Here is what works for me using a custom JsonConverter. I'm a Java developer by day, so perhaps an expert C# dev may be able to improve this or spot any issues.

public class CharEnumConverter<T> : JsonConverter<T>
{
    public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer)
    {
        char c = (char)(int)Enum.Parse(typeof(T), value.ToString());
        writer.WriteValue(c + "");
    }

    public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        string stringValue = (string)reader.Value;
        char charValue = stringValue[0];
        int intValue = (int)charValue;
        string intValueString = intValue + "";

        if (Enum.IsDefined(typeof(T), intValue)) {
            T result = (T)Enum.Parse(typeof(T), intValueString);
            return result;
        } else {
            throw new Exception("Char value [" + charValue + "] is not defined for Enum [" + typeof(T).Name + "]");
        }
    }
}

Example usage on an enum I want to read/write as Json.

[CharEnum]    
public enum MyEnum {
    NONE = 'A',
    YAY = 'B'
}

public class MyClass {

    [JsonConverter(typeof(CharEnumConverter<MyEnum>))]    
    public MyEnum myfield;

}

It will read an 'A' from a JSON string into an enum field with value NONE. It will write an enum field with value YAY to a JSON string as 'B'