4

I am reading a json response from an API which contains information about images.
It is in a dictionary format like so:

images: {
    low_resolution: {
        url: "...",
        width: 150,
        height: 150
    },
    high_resolution: {
        url: "...",
        width: 450,
        height: 450
    }
}

I am deserializing the response into an object, and the images into a Dictionary property like so:

[DataContract()]
public class Post
{
    ...
    [DataMember(Name = "images")]
    public IDictionary<string, Media> Images { get; set; }
    ...
}

HttpResponseMessage response = await client.GetAsync(query);
if (response.IsSuccessStatusCode)
{
    post = await response.Content.ReadAsAsync<Post>();
}

This all works fine so far, but I'd rather deserialize the image resolution information into an enumeration value.
So I created an enumeration ImageResolution and changed the dictionary key from string to ImageResolution.

This also deserializes successfully, as long as the actual enumeration value equals the json string, but I want to change the enum values.
As per various other posts, I have tried the following:

[DataContract()]
public enum ImageResolution
{
    [EnumMember(Value = "low_resolution")]
    Low,

    [EnumMember(Value = "high_resolution")]
    High,
}

Also from searching I have also tried adding:

[JsonConverter(typeof(StringEnumConverter))]

But nothing so far has worked.
There is another property in the response which I am successfully deserializing to an enum and changing the enum value using the JsonConverter attribute, but this is a straight forward property and not a dictionary key, so I am guessing that it being a dictionary key is causing some issues.

Is it possible to deserialize the json value to an enum dictionary key of different text value?

Craig H
  • 2,001
  • 1
  • 14
  • 18

1 Answers1

8

You need to write a CustomConverter for the whole Dictionary as indicated here.

I adapted the code to use EnumMember instead of the prefix used in the other post:

public class DictionaryWithSpecialEnumKeyConverter : JsonConverter
{
    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException();
    }

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

        var valueType = objectType.GetGenericArguments()[1];
        var intermediateDictionaryType = typeof(Dictionary<,>).MakeGenericType(typeof(string), valueType);
        var intermediateDictionary = (IDictionary)Activator.CreateInstance(intermediateDictionaryType);
        serializer.Populate(reader, intermediateDictionary);

        var finalDictionary = (IDictionary)Activator.CreateInstance(objectType);
        foreach (DictionaryEntry pair in intermediateDictionary)
            finalDictionary.Add(ToEnum<ImageResolution>(pair.Key.ToString()), pair.Value);

        return finalDictionary;
    }

    private T ToEnum<T>(string str)
    {
        var enumType = typeof(T);
        foreach (var name in Enum.GetNames(enumType))
        {
            var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single();
            if (enumMemberAttribute.Value == str) return (T)Enum.Parse(enumType, name);
        }
        return default(T);
    }

    public override bool CanConvert(Type objectType)
    {
        return true;
    }
}

And use it on the Dictionary:

 [DataContract]
 public class Post
 {
    [JsonConverter(typeof(DictionaryWithSpecialEnumKeyConverter))]
    [DataMember(Name = "images")]
    public Dictionary<ImageResolution, Media> Images { get; set; }
 }
Community
  • 1
  • 1
Fabian
  • 1,886
  • 14
  • 13
  • @dizel3d Your rejected edit is [being discussed on Meta](http://meta.stackoverflow.com/q/331901/1394393). – jpmc26 Aug 09 '16 at 19:36
  • 1
    how to map this json { Localized : [ { k:"EN", v: "SomeWord" } ] } to this class X{ public Dicationary Localizes {get;set;} } 'Languages' is enum type – Al-Hanash Moataz Aug 27 '20 at 15:45