5

This is my enum type:

public enum AccessScope
{
    [JsonProperty("read_content")]
    ReadContent,

    [JsonProperty("write_content")]
    WriteContent,

    [JsonProperty("read_themes")]
    ReadThemes,

    [JsonProperty("write_themes")]
    WriteThemes
}  

I want to serialize this enum using the string values... when I try the following code, the numeric values are used for serialization:

[TestMethod]
public void Serialize_access_scopes()
{
    var requiredPermissions = new List<AccessScope>()
    {
        AccessScope.ReadContent,
        AccessScope.WriteContent,
    };

    var commaSeparatedPermissions = JsonConvert.SerializeObject(requiredPermissions);  

    commaSeparatedPermissions.Should().Be("read_content, write_content");
}

ialzie

3
  • 1
    [JsonConverter(typeof(StringEnumConverter))] comes a little close, but you might need to write a custom converter in this case; also, the expected output is surely "[\"read_content\", \"write_content\"]"? Commented Mar 3, 2020 at 7:31
  • @MarcGravell: thanks for this... not sure if I understood your comment correctly... I tried adding JsonConverter to the enum but still getting the same numeric result (updated the question) Commented Mar 3, 2020 at 7:45
  • 1
    I meant on the type, but: JsonConverter doesn't do enough here; I've added a custom serializer example Commented Mar 3, 2020 at 7:49

3 Answers 3

12

You could use the StringEnumConverter provided by Json.Net rather than defining a custom converter, along with using the EnumMemberAttribute instead of JsonPropertyAttribute.

For Example,

[JsonConverter(typeof(StringEnumConverter))]
public enum AccessScope
{
    [EnumMember(Value="read_content")]
    ReadContent,

    [EnumMember(Value="write_content")]
    WriteContent,

    [EnumMember(Value="read_themes")]
    ReadThemes,

    [EnumMember(Value="write_themes")]
    WriteThemes
} 

Now you can deserialize as

var requiredPermissions = new List<AccessScope>()
{
    AccessScope.ReadContent,
    AccessScope.WriteContent,
};

var result = JsonConvert.SerializeObject(requiredPermissions);

Output

["read_content","write_content"]
Sign up to request clarification or add additional context in comments.

2 Comments

This answer is a lot more simple, thanks. Would I be able to do the reverse (deserialize) using this method? i.e. pass a comma separated list if string and build a list of enums?
@HoomanBahreini Yes you could do JsonConvert.DeserializeObject<List<AccessScope>>("['read_content','write_content']")
0

You need a custom converter; perhaps:

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;

static class P
{
    class ByNameStringEnumConverter : StringEnumConverter
    {
        static ConcurrentDictionary<Type, (string name, object value)[]> s_byTypeCache = new ConcurrentDictionary<Type, (string name, object value)[]>();

        static (string name, object value)[] GetMap(Type type) => s_byTypeCache.TryGetValue(type, out var result) ? result : AddForType(type);
        static (string name, object value)[] AddForType(Type type)
        {
            var fields = type.GetFields(BindingFlags.Static | BindingFlags.Public);
            var map = Array.ConvertAll(fields, field =>
            {
                string name = field.Name;
                if (Attribute.GetCustomAttribute(field, typeof(JsonPropertyAttribute)) is JsonPropertyAttribute jprop
                    && !string.IsNullOrWhiteSpace(jprop.PropertyName))
                {
                    name = jprop.PropertyName;
                }
                return (name, field.GetValue(null));
            });
            s_byTypeCache.TryAdd(type, map);
            return map;
        }
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.String && reader.Value is string s && objectType.IsEnum)
            {
                var map = GetMap(objectType);
                for (int i = 0; i < map.Length; i++)
                    if (map[i].name == s) return map[i].value;
            }
            return base.ReadJson(reader, objectType, existingValue, serializer);
        }
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            Type type = value?.GetType();
            if (type is object && type.IsEnum)
            {
                var map = GetMap(type);
                for (int i = 0; i < map.Length; i++)
                {
                    if (Equals(map[i].value, value))
                    {
                        writer.WriteValue(map[i].name);
                        return;
                    }
                }
            }
            base.WriteJson(writer, value, serializer);
        }
    }
    [JsonConverter(typeof(ByNameStringEnumConverter))]
    public enum AccessScope
    {
        [JsonProperty("read_content")]
        ReadContent,

        [JsonProperty("write_content")]
        WriteContent,

        [JsonProperty("read_themes")]
        ReadThemes,

        [JsonProperty("write_themes")]
        WriteThemes
    }
    static void Main()
    {
        var requiredPermissions = new List<AccessScope>()
        {
            AccessScope.ReadContent,
            AccessScope.WriteContent,
        };

        var json = JsonConvert.SerializeObject(requiredPermissions);

        Console.WriteLine(json);

        // and back again
        var obj = JsonConvert.DeserializeObject<List<AccessScope>>(json);
        foreach(var val in obj)
            Console.WriteLine(val);
    }
}

Comments

0

Add following namespace in your class

System.Text.Json.Serialization

and also add following annotation above your enum. It will working fine in .NET6

[JsonConverter(typeof(JsonStringEnumConverter))]

using System.Runtime.Serialization;
using System.Text.Json.Serialization;

namespace AppService.Enums
{
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public enum AccessScope
    {
        [EnumMember(Value = "read_content")]
        ReadContent,

        [EnumMember(Value = "write_content")]
        WriteContent,

        [EnumMember(Value = "read_themes")]
        ReadThemes,

        [EnumMember(Value = "write_themes")]
        WriteThemes
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.