40

I have the following enum

public enum PermissionType
{
  [JsonProperty(PropertyName = "can_fly")]
  PermissionToFly,
  [JsonProperty(PropertyName = "can_swim")]
  PermissionToSwim
};

and a class with this property

[JsonProperty(PropertyName = "permissions", ItemConverterType = typeof(StringEnumConverter))]
public IList<PermissionType> PermissionKeynames { get; set; }`

I want to serialize the list of enumerations to a list of strings, and that serialize list use the string specified in PropertyName (such as "can_swim") instead of the property's actual name "PermissionToSwim". However, whenever I call JsonConvert.SerializeObject, I end up with

"permission_keynames":["PermissionToFly","PermissionToSwim"]

instead of my desired

"permission_keynames":["can_fly","can_swim"]

I want to keep the phrase "PermissionToSwim" for use in my code, serialize to another word. Any idea how I can achieve this? My gut says that the annotation is the culprit, but I haven't been able to find the correct one.

pghprogrammer4
  • 898
  • 2
  • 9
  • 21
  • 1
    In case anyone in the future is having the opposite issue (deserializing to an enum with differently-named values), check out [this question](http://stackoverflow.com/questions/7799769/parsing-an-enumeration-in-json-net). – pghprogrammer4 Oct 16 '14 at 13:48

2 Answers2

72

Looks like you can make this work using the EnumMember attribute (found in System.Runtime.Serialization).

public enum PermissionType
{
    [EnumMember(Value = "can_fly")]
    PermissionToFly,

    [EnumMember(Value = "can_swim")]
    PermissionToSwim
}

If you use those attributes you should also not need to set the ItemConverterType in the JsonProperty attribute on the list.

Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
  • Perfect, thank you! I actually saw this earlier but thought it wouldn't work with json.net because it was a System.Runtime.Serialization annotation. Little did I know... – pghprogrammer4 Oct 15 '14 at 20:59
  • 7
    This worked for me, except, regarding your last comment, I still needed to use the StringEnumConverter -- otherwise, it went back to deserializing to integers. – Gyromite Nov 21 '17 at 00:42
  • 4
    I agree with @Gyromite. That's what I had to do it to make it work: 1) to serialize: `var js = JsonConvert.SerializeObject(PermissionType.PermissionToFly, new StringEnumConverter());` 2) to deserialize: `var permissionType = JsonConvert.DeserializeObject(js, new StringEnumConverter());` – Mykhailo Seniutovych Apr 06 '18 at 14:25
  • 1
    Note that this is not Json.NET-specific and may also affect other methods of serializing the enum such as XML serialization. – xr280xr Jun 09 '21 at 03:47
1

You can define custom converter for serializing enums by JsonProperty attribute with System.Reflection.

public class EnumByAttributesConverter<TEnum> : JsonConverter<TEnum>
    where TEnum : struct, Enum
{
    public override void WriteJson(JsonWriter writer, TEnum value, JsonSerializer serializer)
    {
        var valueName = value.ToString();
        var membersAttributes = GetMemberAttributePairs(value.GetType());
        var propertyName = membersAttributes.FirstOrDefault(item => item.Name == valueName)
            .Attribute?.PropertyName;
        writer.WriteValue(propertyName ?? valueName);
    }

    public override TEnum ReadJson(JsonReader reader, Type objectType, TEnum existingValue, 
        bool hasExistingValue, JsonSerializer serializer)
    {
        var name = reader.Value as string;
        var membersAttributes = GetMemberAttributePairs(objectType);
        var memberName = membersAttributes
            .FirstOrDefault(item => item.Attribute.PropertyName == name).Name;
        return Enum.TryParse<TEnum>(memberName ?? name, out var parsedResult)
            ? parsedResult
            : default;
    }

    private IReadOnlyCollection<(string Name, JsonPropertyAttribute Attribute)> GetMemberAttributePairs(Type type) =>
        type.GetMembers()
            .Select(member => (member.Name,
                Attribute: member.GetCustomAttributes(typeof(JsonPropertyAttribute), false)
                        .FirstOrDefault() as JsonPropertyAttribute))
            .Where(p => p.Attribute != null).ToArray();
}

You still have to define JsonConverter attribute, but that way avoid to mix System.Runtime.Serialization and Newtonsoft.Json in project.