将枚举序列化为字符串

问题描述 投票:0回答:10

我有一个枚举:

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}
(我需要枚举的 ToString 形式而不是 int)

我可以在不向班级添加其他成员的情况下完成此操作吗?

c# json datacontract
10个回答
38
投票

您只需添加属性即可:

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

36
投票
使用

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());
    

30
投票
这里有一个简单的方法:

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

23
投票
JSON 格式化程序在处理枚举时具有非常专门的行为;普通的数据契约属性将被忽略,并将您的枚举视为数字,而不是您期望的其他格式的更易于人类阅读的字符串。虽然这使得处理标志类型枚举变得容易,但它使大多数其他类型更难处理。

来自

MSDN

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

  • 例如,如果您有

    public enum Color {red, green, blue, yellow, pink}

    ,则序列化 Yellow 会生成数字 3 而不是字符串
      “黄色”。

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

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

  • 标志枚举并不特殊,其处理方式与任何其他枚举相同。

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

或者(尽管不是假装的),您可以将 JSON 格式化程序替换为您自己的格式化程序,这将像其他格式化程序一样尊重枚举。


4
投票
我一直在使用一个非常好的解决方法,即使用辅助私有属性进行序列化和反序列化,该属性可用于按枚举成员名称或按

EnumMemberAttribute

 的值进行序列化。

我看到的最大优点是:

    您不需要使用序列化器进行调整
  • 所有序列化逻辑都包含在数据对象中
  • 您可以通过将辅助属性的可访问性修饰符设置为 private 来隐藏辅助属性,因为
  • 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); } }
    

2
投票
如果您使用 .Net 本机 json 序列化程序,即 System.Text.Json.Serialization,那么您可以在 enum 上添加一个属性,以便将 enum 转换为字符串而不是 int。

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

[JsonConverter(typeof(JsonStringEnumConverter))]


0
投票
尝试使用

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

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

此处描述:

http://msdn.microsoft.com/en-us/library/aa347875.aspx


0
投票
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;}
    

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

容器定义

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

0
投票
我已经使用

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

© www.soinside.com 2019 - 2024. All rights reserved.