C# 将枚举序列化为字符串

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9156820/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 06:11:38  来源:igfitidea点击:

Serialize enum to string

c#jsondatacontract

提问by Naor

I have an enum:

我有一个枚举:

public enum Action {
    Remove=1,
    Add=2
}

And a class:

还有一个类:

[DataContract]
public class Container {
    [DataMember]
    public Action Action {get; set;}
}

When serialize instance of Container to json I get: {Action:1}(in case Action is Remove).

当将 Container 的实例序列化为 json 时,我得到:({Action:1}如果 Action 是 Remove)。

I would like to get: {Action:Remove}(instead of int I need to ToString form of the enum)

我想得到:({Action:Remove}而不是 int 我需要枚举的 ToString 形式)

Can I do it without adding another member to the class?

我可以在不向班级中添加其他成员的情况下进行吗?

采纳答案by Paul Turner

The JSON formatter has very specialized behaviour when working with enumerations; the normal Data Contract attributes are ignored and it treats your enum as a number, not the more human-readable string you'd expect with other formats. Whilst this makes it easy to deal with flag-type enumerations, it makes most other types much harder to work with.

JSON 格式化程序在处理枚举时具有非常特殊的行为;正常的数据协定属性被忽略,它将您的枚举视为一个数字,而不是您期望使用其他格式的更易读的字符串。虽然这使得处理标志类型枚举变得容易,但它使大多数其他类型更难以使用。

From MSDN:

MSDN

Enumeration member values are treated as numbers in JSON, which is different from how they are treated in data contracts, where they are included as member names. For more information about the data contract treatment, see Enumeration Types in Data Contracts.

  • For example, if you have public enum Color {red, green, blue, yellow, pink}, serializing yellow produces the number 3 and not the string "yellow".

  • All enum members are serializable. The EnumMemberAttribute and the NonSerializedAttribute attributes are ignored if used.

  • It is possible to deserialize a nonexistent enum value - for example, the value 87 can be deserialized into the previous Color enum even though there is no corresponding color name defined.

  • A flags enum is not special and is treated the same as any other enum.

枚举成员值在 JSON 中被视为数字,这与它们在数据协定中的处理方式不同,在数据协定中它们被包含为成员名称。有关数据协定处理的更多信息,请参阅数据协定中的枚举类型

  • 例如,如果您有public enum Color {red, green, blue, yellow, pink},序列化黄色会产生数字 3 而不是字符串“yellow”。

  • 所有枚举成员都是可序列化的。如果使用 EnumMemberAttribute 和 NonSerializedAttribute 属性,则会被忽略。

  • 可以反序列化不存在的枚举值 - 例如,即使没有定义相应的颜色名称,也可以将值 87 反序列化为之前的 Color 枚举。

  • 标志枚举并不特殊,并且与任何其他枚举一样对待。

The only practical way to resolve this, to allow end-users to specify a string instead of a number, is to not use the enum in your contract. Instead the practical answer is to replace your enum with a string and perform internal validation on the value such that it can be parsed into one of the valid enum representations.

解决此问题的唯一实用方法,即允许最终用户指定字符串而不是数字,是不在合同中使用枚举。相反,实际的答案是用字符串替换枚举并对值执行内部验证,以便将其解析为有效的枚举表示形式之一。

Alternatively (though not for the feint of heart), you could replace the JSON formatter with your own, which would respect enumerations in the same way as other formatters.

或者(虽然不是装腔作势),您可以将 JSON 格式化程序替换为您自己的格式化程序,这将与其他格式化程序一样尊重枚举。

回答by Michal B.

Try using

尝试使用

public enum Action {
    [EnumMember(Value = "Remove")]
    Remove=1,
    [EnumMember(Value = "Add")]
    Add=2
}

I am not sure if this suits your case though, so I might be wrong.

我不确定这是否适合你的情况,所以我可能是错的。

It's described here: http://msdn.microsoft.com/en-us/library/aa347875.aspx

它在这里描述:http: //msdn.microsoft.com/en-us/library/aa347875.aspx

回答by L.B

Using Json.Net, you can define a custom StringEnumConverteras

使用 Json.Net,您可以将自定义定义StringEnumConverter

public class MyStringEnumConverter : Newtonsoft.Json.Converters.StringEnumConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value is Action)
        {
            writer.WriteValue(Enum.GetName(typeof(Action),(Action)value));// or something else
            return;
        }

        base.WriteJson(writer, value, serializer);
    }
}

and serialize as

并序列化为

string json=JsonConvert.SerializeObject(container,new MyStringEnumConverter());

回答by Theo Koekemoer

The solution posted by Michal B works good. Here is another example.

Michal B 发布的解决方案效果很好。这是另一个例子。

You would need to do the Following as the Description Attribute is not serializable.

您需要执行以下操作,因为 Description 属性不可序列化。

[DataContract]
public enum ControlSelectionType
{
    [EnumMember(Value = "Not Applicable")]
    NotApplicable = 1,
    [EnumMember(Value = "Single Select Radio Buttons")]
    SingleSelectRadioButtons = 2,
    [EnumMember(Value = "Completely Different Display Text")]
    SingleSelectDropDownList = 3,
}


public static string GetDescriptionFromEnumValue(Enum value)
{
    EnumMemberAttribute attribute = value.GetType()
        .GetField(value.ToString())
        .GetCustomAttributes(typeof(EnumMemberAttribute), false)
        .SingleOrDefault() as EnumMemberAttribute;
    return attribute == null ? value.ToString() : attribute.Value;}

回答by ZenLulz

For serialization purpose, if the container must not contain enumeration properties but are filled with, you can use the extension method below.

出于序列化的目的,如果容器不能包含枚举属性但已填充,则可以使用下面的扩展方法。

Container definition

容器定义

public class Container
{
    public string Action { get; set; }
}

Enumeration definition

枚举定义

public enum Action {
    Remove=1,
    Add=2
}

Code in views

视图中的代码

@Html.DropDownListFor(model => model.Action, typeof (Action))

Extension method

扩展方式

/// <summary>
/// Returns an HTML select element for each property in the object that is represented by the specified expression using the given enumeration list items.
/// </summary>
/// <typeparam name="TModel">The type of the model.</typeparam>
/// <typeparam name="TProperty">The type of the value.</typeparam>
/// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
/// <param name="expression">An expression that identifies the object that contains the properties to display.</param>
/// <param name="enumType">The type of the enum that fills the drop box list.</param>
/// <returns>An HTML select element for each property in the object that is represented by the expression.</returns>
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TProperty>> expression, Type enumType)
{
    var values = from Enum e in Enum.GetValues(enumType)
                    select new { Id = e, Name = e.ToString() };

    return htmlHelper.DropDownListFor(expression, new SelectList(values, "Id", "Name"));
}

回答by ShaTin

Here's a simple way to do this:

这是一个简单的方法来做到这一点:

JsonConvert.SerializeObject(myObject, Formatting.Indented, new StringEnumConverter());

回答by GPR

You can just add the attribute:

您只需添加属性:

    [Newtonsoft.Json.JsonConverter(typeof(StringEnumConverter))] 

to the enum property that is not serializing as a string.

到未序列化为字符串的枚举属性。

or if you have a more exotic formattting in mind you could use the attributes as below to tell the JSON serializer to serialise only the property that you have formatted as you wish. Depends a bit on the rest of your implementation. It recognises the DataMember attribute on a property as well.

或者,如果您有更奇特的格式,您可以使用以下属性来告诉 JSON 序列化程序仅序列化您希望格式化的属性。有点取决于您的其余实现。它也识别属性上的 DataMember 属性。

[JsonObject(MemberSerialization = MemberSerialization.OptOut)]
public class Container
{
    public Action Action { get; set; }

    [JsonProperty(PropertyName = "Action")]
    public string ActionString
    {
        get
        {
            return Action.ToString();
        }
    }
}

回答by Jon Grant

I have put a solution to this using the Newtonsoft.Jsonlibrary. It fixes the enum issue and also makes the error handling much better, and it works in IIS hosted services rather than self-hosted ones. It requires no changes or anything special to be added to your DataContractclasses. It's quite a lot of code, so you can find it on GitHub here: https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

我已经使用Newtonsoft.Json库解决了这个问题。它修复了枚举问题并使错误处理更好,并且它适用于 IIS 托管服务而不是自托管服务。它不需要更改或添加任何特殊内容到您的DataContract课程中。这是相当多的代码,所以你可以在 GitHub 上找到它:https: //github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

You have to add some entries to your Web.configto get it to work, you can see an example file here: https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config

你必须添加一些条目Web.config才能让它工作,你可以在这里看到一个示例文件:https: //github.com/jongrant/wcfjsonserializer/blob/master/Web.config

回答by Andre Soares

I've been using a very good workaround by using an auxiliary private property for serialization and deserialization that works either for serialization by the enum member name or by the value of the EnumMemberAttribute.

我一直在使用一个非常好的解决方法,通过使用辅助私有属性进行序列化和反序列化,该属性既可以通过枚举成员名称进行序列化,也可以通过EnumMemberAttribute.

The greatest advantages I see, are that:

我看到的最大优点是:

  • You don't need to tweak with the serializer
  • All the serialization logic is contained in the Data Object
  • You can hide your auxiliary property by setting it's accessibility modifier to private, since the DataContractSerializersare able to get and set private properties
  • You are able to serialize the enum as a stringinstead of an int
  • 您不需要使用序列化程序进行调整
  • 所有的序列化逻辑都包含在数据对象中
  • 您可以通过将辅助属性的可访问性修饰符设置为私有来隐藏辅助属性,因为DataContractSerializers能够获取和设置私有属性
  • 您可以将枚举序列化为 astring而不是int

Your class will look like this:

你的类看起来像这样:

[DataContract]
public class SerializableClass {
    public Shapes Shape {get; set;} //Do not use the DataMemberAttribute in the public property

    [DataMember(Name = "shape")]
    private string ShapeSerialization // Notice the PRIVATE here!
    {
        get { return EnumHelper.Serialize(this.Shape); }
        set { this.Shape = EnumHelper.Deserialize<Shapes>(value); }
    }
}

EnumHelper.cs

EnumHelper.cs

/* Available at: https://gist.github.com/mniak/a4d09264ad1ca40c489178325b98935b */
public static class EnumHelper
{
    public static string Serialize<TEnum>(TEnum value)
    {
        var fallback = Enum.GetName(typeof(TEnum), value);
        var member = typeof(TEnum).GetMember(value.ToString()).FirstOrDefault();
        if (member == null)
            return fallback;
        var enumMemberAttributes = member.GetCustomAttributes(typeof(EnumMemberAttribute), false).Cast<EnumMemberAttribute>().FirstOrDefault();
        if (enumMemberAttributes == null)
            return fallback;
        return enumMemberAttributes.Value;
    }
    public static TEnum Deserialize<TEnum>(string value) where TEnum : struct
    {
        TEnum parsed;
        if (Enum.TryParse<TEnum>(value, out parsed))
            return parsed;

        var found = typeof(TEnum).GetMembers()
            .Select(x => new
            {
                Member = x,
                Attribute = x.GetCustomAttributes(typeof(EnumMemberAttribute), false).OfType<EnumMemberAttribute>().FirstOrDefault()
            })
            .FirstOrDefault(x => x.Attribute?.Value == value);
        if (found != null)
            return (TEnum)Enum.Parse(typeof(TEnum), found.Member.Name);
        return default(TEnum);
    }
}