С# Json.NET Render Flags Enum as String Array

В .NET приложении у меня есть набор значений, которые хранятся как [Flags] enum. Я хочу сериализовать их для json, но вместо того, чтобы результат был целым, я хотел бы получить массив строк для активных флагов.

Итак, если у меня есть следующий код

[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 });
}

Я хочу вернуть метод Serialize():

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

Вместо:

"{ Flags: 5 }"

Ответ 1

Вы должны реализовать свой собственный конвертер. Вот пример (особенно грязный и хакерский способ сделать это, но он служит хорошей демонстрацией):

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;
    }
}

Теперь украсьте свое перечисление следующим образом:

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

И ваш пример кода сериализации теперь выдает следующее:

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

Ответ 2

Украсьте свой enum

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

Вывод:

{ "Флаги": "Val1, Val4" }

Я понимаю, что JSON не является массивом, как в вашем вопросе, не был уверен, что это необходимо, так как это также верно JSON.

Ответ 3

Этот ответ немного отличается от сериализации, запрошенной OP, но, тем не менее, может быть полезен. Он основан на решении @davidg, но сериализованный JSON выглядит следующим образом (для 1 + 4 = 5):

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

Оформление перечислений флагов такое же, как в ответе Давида:

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

Но здесь другой метод WriteJson и минимальный рабочий пример для метода ReadJson.

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;
    }
}

Ответ 4

 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;

        }

Использовать лайк

ConvertEnumsToJson<byte>(typeof(AnyEnum))

Ответ 5

Я использовал ответ @DavidG выше, но мне нужна была реализация для ReadJson. Вот что я собрал:

        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;
        }