Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize json character as enumeration

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?

like image 299
SelAromDotNet Avatar asked Aug 31 '13 18:08

SelAromDotNet


3 Answers

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'
}
like image 131
nemesv Avatar answered Nov 08 '22 16:11

nemesv


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; }
like image 44
Vladimir Avatar answered Nov 08 '22 14:11

Vladimir


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");
    }
}
like image 45
David-Paul Brown Avatar answered Nov 08 '22 16:11

David-Paul Brown