将枚举序列化为字符串

Posted

技术标签:

【中文标题】将枚举序列化为字符串【英文标题】:Serialize enum to string 【发布时间】:2012-02-27 17:36:30 【问题描述】:

我有一个枚举:

public enum Action 
    Remove=1,
    Add=2

还有一个班级:

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

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

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

我可以在不向班级添加其他成员的情况下这样做吗?

【问题讨论】:

***.com/a/2870420/577167 对面的解码器是否支持将字符串解码成枚举?你为什么需要这个? 有一个可行的解决方案如何***.com/questions/2121010/… JSON serialization of c# enum as string 的可能副本 【参考方案1】:

使用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);
    

并序列化为

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

【讨论】:

不错的一个。我们需要通过 JSON 将枚举值发送到客户端并返回到服务器,而用户无法进行选择。自定义格式化程序是要走的路。谢谢L.B +1 你可以为每个枚举做,不仅仅是你的:if (value.GetType().IsEnum) writer.WriteValue(Enum.GetName(value.GetType(), value)); return; 其实……你甚至不需要编写这个类——你可以传入“new Newtonsoft.Json.Converters.StringEnumConverter()”——它还有工作的额外好处在所有枚举上,并正确反序列化它们(不知道为什么,但上面的类没有正确反序列化枚举,即使它只是被调用的基类方法)。 结合@Gman 和原始答案,它非常适合我有多个 XML 文档解析的用例。【参考方案2】:

你可以只添加属性:

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

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

或者,如果您有更奇特的格式设置,您可以使用以下属性告诉 JSON 序列化程序仅序列化您已根据需要格式化的属性。取决于您的实施的其余部分。它还可以识别属性上的 DataMember 属性。

[JsonObject(MemberSerialization = MemberSerialization.OptOut)]
public class Container

    public Action Action  get; set; 

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

【讨论】:

仅供参考 命名空间略有变化:[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]【参考方案3】:

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

来自MSDN:

枚举成员值在 JSON 中被视为数字,即 不同于它们在数据合同中的处理方式,它们在哪里 包括作为成员名称。有关数据合同的更多信息 治疗,见Enumeration Types in Data Contracts。

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

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

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

标志枚举不是特殊的,并且与任何其他枚举相同。

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

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

【讨论】:

【参考方案4】:

这是一个简单的方法:

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

【讨论】:

这可能是迄今为止最简单的解决方案。谢谢! 最简单的解决方案。谢谢!【参考方案5】:

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

我看到的最大优势是:

您无需使用序列化程序进行调整 所有序列化逻辑都包含在数据对象中 您可以通过将辅助属性的可访问性修饰符设置为私有来隐藏辅助属性,因为 DataContractSerializers 能够 获取和设置私有属性 您可以将枚举序列化为string 而不是int

您的课程将如下所示:

[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

/* 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);
    

【讨论】:

【参考方案6】:

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

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

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

【讨论】:

【参考方案7】:

尝试使用

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

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

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

【讨论】:

这被记录为 JSON 格式化程序不支持:msdn.microsoft.com/en-us/library/bb412170.aspx 在 Web Api 2.1 项目中与 Newtonsoft.Json.Converters.StringEnumConverter 一起为我工作【参考方案8】:

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

容器定义

public class Container

    public string Action  get; set; 

枚举定义

public enum Action 
    Remove=1,
    Add=2

视图中的代码

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

扩展方法

/// <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"));

【讨论】:

【参考方案9】:

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

您必须在Web.config 中添加一些条目才能使其正常工作,您可以在此处查看示例文件: https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config

【讨论】:

【参考方案10】:

如果您使用的是 .Net 原生 json 序列化程序,即 System.Text.Json.Serialization,那么您可以在 enum 上添加一个属性,以便它将 enum 转换为 string 而不是 int。

您应该将以下属性添加到您想要作为字符串的枚举中

[JsonConverter(typeof(JsonStringEnumConverter))]

【讨论】:

以上是关于将枚举序列化为字符串的主要内容,如果未能解决你的问题,请参考以下文章

将json字符反序列化为枚举

ASP.NET MVC Core API 将枚举序列化为字符串

Newton.JSON将数字序列化为枚举项[重复]

从 JSON 反序列化 java 枚举

将枚举成员序列化为 JSON

爪哇 |将字符串持久化为枚举