簡體   English   中英

Json.NET自定義帶有數據注釋的枚舉類型序列化

[英]Json.NET custom serialization of a enum type with data annotation

我想序列化枚舉類型,以便它返回一個數組,其中枚舉作為一個對象,包含“值”,“名稱”和數據注釋值。 我需要序列化方面的幫助。 這是我到目前為止所做的:枚舉:

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序列化和反序列化以獲得所需的輸出。 任何幫助都會受到影響。

好吧,這可能會被清理一下,但我會寫兩個自定義轉換器:一個用於Enum類型,另一個用於枚舉值:

我創建了一個自定義類來序列化到您想要的最終結果:

public class EnumValue
{
    public int Value { get; set; }

    public string Name { get; set; }

    public string Label { get; set; }
}

除了一個靜態類,它可以執行一些從Enum和枚舉值創建該類型實例的工作:

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 ,我們需要用它來裝飾我們的enum對象。 因此,我們已經聲明的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"}
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM