简体   繁体   中英

C# Json.NET Render Flags Enum as String Array

In a .NET Application, I have a set of values that are stored as a [Flags] enum . I want to serialize these to json, but instead of having the result be an integer, I'd like to get an array of string for the flags that are active.

So if I have the following code

[Flags]
public enum F
{
    Val1 = 1,
    Val2 = 2,
    Val4 = 4,
    Val8 = 8

}

public class C
{        
    public F Flags { get; set; }
}

string Serialize() {
    return JsonConvert.SerializeObject(new C { Flags = F.Val1 | F.Val4 });
}

I want the Serialize() method to return:

"{ Flags: [ "Val1", "Val4" ] }"

Instead of:

"{ Flags: 5 }"

You have to implement your own converter. Here's an example (a particularly dirty and hacky way of doing it, but it serves as a good demo):

public class FlagConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader,  Type objectType, Object existingValue, JsonSerializer serializer)
    {
        //If you need to deserialize, fill in the code here
        return null;
    }

    public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer)
    {
        var flags = value.ToString()
            .Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries)
            .Select(f => $"\"{f}\"");

        writer.WriteRawValue($"[{string.Join(", ", flags)}]");
    }

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

Now decorate your enum like this:

[Flags]
[JsonConverter(typeof(FlagConverter))]
public enum F
{
    Val1 = 1,
    Val2 = 2,
    Val4 = 4,
    Val8 = 8
}

And your example serialisation code will now output this:

{"Flags":["Val1", "Val4"]}

Decorate your enum

[Flags]
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public enum F
{
    Val1 = 1,
    Val2 = 2,
    Val4 = 4,
    Val8 = 8
}

Output:

{"Flags":"Val1, Val4"}

I realise the JSON is not an array as in your question, wasn't sure if this was required since this is also valid JSON.

I used @DavidG's answer above, but needed an implementation for ReadJson. Here's what I put together:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    int outVal = 0;
    if (reader.TokenType == JsonToken.StartArray)
    {
        reader.Read();
        while (reader.TokenType != JsonToken.EndArray)
        {
            outVal += (int)Enum.Parse(objectType, reader.Value.ToString());
            reader.Read();
        }
    }
    return outVal;
}

This answer slightly differs from the serialization requested by OP but might be useful nonetheless. It's based on the solution by @davidg but the serialized JSON looks like this (for 1+4=5):

{
    "Val1": true,
    "Val2": false,
    "Val4": true,
    "Val8": false  
}

The decoration of the flags enum is the same as in Davids answer:

[Flags]
[JsonConverter(typeof(FlagConverter))]
public enum F
{
    Val1 = 1,
    Val2 = 2,
    Val4 = 4,
    Val8 = 8
}

But here's a different WriteJson method and a minimal working example for a ReadJson method.

public class FlagConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        JObject jobject = JObject.FromObject(token);
        F result = 0;
        foreach (F f in Enum.GetValues(typeof(F)))
        {
            if (jobject[f.ToString()] != null && (bool)jobject[f.ToString()])
            {
                result |= f; // key is present and value is true ==> set flag
            }
        }
        return result;
    }

    public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer)
    {
        JObject result = new JObject();
        F f = (F)value;
        foreach (F f in Enum.GetValues(typeof(F)))
        {
            result[f.ToString()] = status.HasFlag(f);
        }
        writer.WriteRawValue(JsonConvert.SerializeObject(result));
    }

    public override bool CanConvert(Type objectType)
    {
        return true;
    }
}
 public static string ConvertEnumsToJson<T>(Type e)
        {

            var ret = "{";
            var index = 0;
            foreach (var val in Enum.GetValues(e))
            {
                if (index > 0)
                {
                    ret += ",";
                }
                var name = Enum.GetName(e, val);
                ret += name + ":" + ((T)val) ;
                index++;
            }
            ret += "}";
            return ret;

        }

Use Like

ConvertEnumsToJson<byte>(typeof(AnyEnum))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM