pghprogrammer4
pghprogrammer4

Reputation: 948

How to serialize enums to different property name using json.net

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.

Upvotes: 45

Views: 39072

Answers (2)

Andrey Nikitin
Andrey Nikitin

Reputation: 51

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.

Upvotes: 1

Andrew Whitaker
Andrew Whitaker

Reputation: 126042

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.

Upvotes: 81

Related Questions