Json.NET 具有数据注释的枚举类型的自定义序列化

本文关键字:枚举 类型 自定义 序列化 注释 NET 数据 Json | 更新日期: 2023-09-27 18:20:39

我想序列化一个枚举类型,以便它返回一个数组,其中枚举作为包含"值"、"名称"和数据注释值的对象。我需要序列化方面的帮助。这是我到目前为止所做的:枚举:

public enum Status
{
    [Display(Name="Active status")]
    Active = 1,
    [Display(Name = "Deactive status")]
    Deactive = 2,
    [Display(Name = "Pending status")]
    Pending = 3
}

应序列化的 DTO 对象:

public class ProjectDto
{
    public Type StatusEnum { get; set; }
    public Status CurrentStatus { get; set; }
}

值的分配:

var project = new ProjectDto
{
    CurrentStatus = Status.Active, 
    StatusEnum = typeof (Status)
};
var output = JsonConvert.SerializeObject(project);

要从我使用的枚举中获取值:

Enum.GetNames(typeof(Status)) //To get the names in the enum
Enum.GetValues(typeof(Status)) //To get the values in the enum

获取数据注释名称值有点棘手,但我在本文中找到了帮助:http://geeksharp.com/2011/11/02/power-up-your-enumerations/他们创建了一个帮助程序方法,该方法将使用以下命令获取写入数据注释中的值:

public static string GetAttributeValue<T>(this Enum e,
    Func<T, object> selector) where T : Attribute
{
    var output = e.ToString();
    var member = e.GetType().GetMember(output).First();
    var attributes = member.GetCustomAttributes(typeof(T), false);
    if (attributes.Length > 0)
    {
        var firstAttr = (T)attributes[0];
        var str = selector(firstAttr).ToString();
        output = string.IsNullOrWhiteSpace(str) ? output : str;
    }
    return output;
}

您可以使用以下方法获取值:

.GetAttributeValue<DisplayAttribute>(y => y.Name)

输出应类似于

{
    statusEnum: [
        { "value": "1", "name": "Active", "label": "Active status" },
        { "value": "2", "name": "Deactive", "label": "Deactive status" },
        { "value": "3", "name": "Pending", "label": "Pending status" }
    ],
    currentStatus: { "value": "1", "name": "Active", "label": "Active status" }
}

如前所述,我需要帮助创建自定义 Json.NET 序列化和反序列化以获得所需的输出。任何帮助都将被征用。

Json.NET 具有数据注释的枚举类型的自定义序列化

好的,这可能可以清理一下,但我会编写两个自定义转换器:一个用于Enum类型,另一个用于枚举值:

我创建了一个自定义类来序列化为您想要的最终结果:

public class EnumValue
{
    public int Value { get; set; }
    public string Name { get; set; }
    public string Label { get; set; }
}

以及一个静态类,该类执行一些从 Enum s 和枚举值创建该类型实例的工作:

public static class EnumHelpers
{
    public static EnumValue GetEnumValue(object value, Type enumType)
    {
        MemberInfo member = enumType.GetMember(value.ToString())[0];
        DisplayAttribute attribute = 
            member.GetCustomAttribute<DisplayAttribute>();
        return new EnumValue
        {
            Value = (int)value,
            Name = Enum.GetName(enumType, value),
            Label = attribute.Name
        };
    }
    public static EnumValue[] GetEnumValues(Type enumType)
    {
        Array values = Enum.GetValues(enumType);
        EnumValue[] result = new EnumValue[values.Length];
        for (int i = 0; i < values.Length; i++)
        {
            result[i] = GetEnumValue(
                values.GetValue(i),
                enumType);
        }
        return result;
    }
}

然后有两个转换器类。第一个将System.Type序列化为所需的对象:

public class EnumTypeConverter : JsonConverter
{
    public override void WriteJson(
        JsonWriter writer,
        object value,
        JsonSerializer serializer)
    {
        if (value == null)
        {
            writer.WriteNull();
            return;
        }
        EnumValue[] values = EnumHelpers.GetEnumValues((Type)value);
        serializer.Serialize(writer, values);
    }
    public override object ReadJson(
        JsonReader reader,
        Type objectType,
        object existingValue,
        JsonSerializer serializer)
    {
        throw new NotSupportedException();
    }
    public override bool CanRead { get { return false; } }
    public override bool CanConvert(Type objectType)
    {
        return typeof(Type).IsAssignableFrom(objectType);
    }
}

然后是序列化实际枚举值的那个:

public class EnumValueConverter : JsonConverter
{
    public override void WriteJson(
        JsonWriter writer,
        object value,
        JsonSerializer serializer)
    {
        if (value == null)
        {
            writer.WriteNull();
            return;
        }
        EnumValue result = EnumHelpers.GetEnumValue(value, value.GetType());
        serializer.Serialize(writer, result);
    }
    public override object ReadJson(
        JsonReader reader,
        Type objectType,
        object existingValue,
        JsonSerializer serializer)
    {
        throw new NotSupportedException();
    }
    public override bool CanRead { get { return false; } }
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsEnum;
    }
}

以下是您将如何使用所有这些:

var pr = new ProjectDto();
pr.CurrentStatus = Status.Active;
pr.StatusEnum = typeof(Status);
var settings = new JsonSerializerSettings();
settings.Converters = new JsonConverter[] 
{
    new EnumTypeConverter(),
    new EnumValueConverter()
};
settings.Formatting = Newtonsoft.Json.Formatting.Indented;
string serialized = JsonConvert.SerializeObject(pr, settings);

示例:https://dotnetfiddle.net/BVp7a2

这是我在面对相同问题时经常采取的方法。(这是一个小提琴,如果你想直接跳到一个工作示例(

设置枚举

我经常发现自己需要枚举的替代值。出于这个原因,我喜欢创建一个属性来存储这些替代值。例如:

[AttributeUsage(AttributeTargets.Field)]
public class AlternativeValueAttribute : Attribute
{
    public string JsonValue { get; set; }
    public string DbValue { get; set; }
    // and any other kind of alternative value you need...
}

(请注意,DbValue属性与本演示的目的无关...这只是为了演示持有多个替代值。

在构建我的枚举对象时,我对需要替代值的每个值使用此属性。例如:

public enum ObjectState
{
    [AlternativeValue(DbValue = "-1", JsonValue="is-unknown")]
    Unknown,
    [AlternativeValue(DbValue = "1", JsonValue="is-active")]
    Active, 
    [AlternativeValue(DbValue = "0", JsonValue="is-inactive")]
    Inactive
    // ...
}

创建转换器

现在我们需要创建一个转换器,以便能够利用替代值。在本例中,我们正在序列化/反序列化 Json,因此我们将创建一个JsonConverter

public class AlternativeValueJsonConverter<TEnum> : JsonConverter where TEnum : struct, IConvertible, IComparable, IFormattable
{
    public override bool CanConvert( Type objectType )
    {
        // we can only convert if the type of object matches the generic type specified
        return objectType == typeof( TEnum );
    }
    public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
    {
        if( objectType == typeof(TEnum) )
        {
            // cycle through the enum values
            foreach(var item in (TEnum[])Enum.GetValues( typeof( TEnum ) ) )
            {
                // get the AlternativeValueAttribute, if it exists
                var attr = item.GetType().GetTypeInfo().GetRuntimeField( item.ToString() )
                    .GetCustomAttribute<AlternativeValueAttribute>();
                // if the JsonValue property matches the incoming value, 
                // return this enum value
                if (attr != null && attr.JsonValue == reader.Value.ToString())
                {
                    return item;
                }
            }
        }
        return null;
    }
    public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
    {
        if( value.GetType() == typeof( TEnum ) )
        {
            // cycle through the enum values
            foreach( var item in (TEnum[])Enum.GetValues( typeof( TEnum ) ) )
            {
                // if we've found the right enum value
                if (item.ToString() == value.ToString() )
                {
                    // get the attribute from the enum value
                    var attr = item.GetType().GetTypeInfo().GetRuntimeField( item.ToString() )
                        .GetCustomAttribute<AlternativeValueAttribute>();
                    if( attr != null)
                    {
                        // write out the JsonValue property's value
                        serializer.Serialize( writer, attr.JsonValue );
                    }
                }
            }
        }
    }
}

用法

最后,要使用这个JsonConverter,我们需要用它装饰我们的枚举对象。因此,我们已经声明的ObjectState枚举应该更新为使用转换器。例如:

[JsonConverter(typeof(AlternativeValueJsonConverter<ObjectState>))]
public enum ObjectState
{
    [AlternativeValue(DbValue = "-1", JsonValue="is-unknown")]
    Unknown,
    [AlternativeValue(DbValue = "1", JsonValue="is-active")]
    Active, 
    [AlternativeValue(DbValue = "0", JsonValue="is-inactive")]
    Inactive
    // ...
}

现在,出于演示目的,我们将创建一个包含ObjectState枚举的简单 POCO 并将其转换为 Json,以确保我们获得预期的结果:

public class DemoPoco
{
    public ObjectState MyObjectState { get; set; }
}
public static void Main( string[] args )
{
    DemoPoco demo = new DemoPoco { MyObjectState = ObjectState.Active };
    var json = JsonConvert.SerializeObject( demo );
    Console.WriteLine(json); // output: {"MyObjectState":"is-active"}
}