我有一个枚举:
public enum Action {
Remove=1,
Add=2
}
一堂课:
[DataContract]
public class Container {
[DataMember]
public Action Action {get; set;}
}
将Container的实例序列化为json时,我得到:{Action:1}
(如果Action被移除)。
我想得到:{Action:Remove}
(而不是int我需要ToString形式的枚举)
我可以不在课程中添加其他成员吗?
答案 0 :(得分:30)
使用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());
答案 1 :(得分:21)
使用枚举时,JSON格式化程序具有非常专业的行为;正常的数据协定属性被忽略,它将您的枚举视为一个数字,而不是您期望与其他格式相关的更易读的字符串。虽然这使得处理标志类型枚举变得容易,但它使大多数其他类型更难以使用。
来自MSDN:
枚举成员值被视为JSON中的数字,即 不同于它们在数据合同中的处理方式 包含在成员名称中。有关数据协定的更多信息 治疗,见Enumeration Types in Data Contracts。
例如,如果您有
public enum Color {red, green, blue, yellow, pink}
,序列化黄色会产生数字3,而不是字符串 “黄色”。所有枚举成员都是可序列化的。 EnumMemberAttribute和 如果使用,则忽略NonSerializedAttribute属性。
可以反序列化不存在的枚举值 - 例如, 值87可以反序列化为之前的Color枚举 虽然没有定义相应的颜色名称。
标志枚举不是特殊的,并且与任何其他枚举一样对待。
解决此问题的唯一实用方法是允许最终用户指定字符串而不是数字,这是在合同中不使用枚举。相反,实际的答案是用字符串替换你的枚举并对值执行内部验证,以便可以将其解析为一个有效的枚举表示。
或者(虽然不是为了佯攻),你可以用自己的格式化替换JSON格式化程序,它会像其他格式化程序一样尊重枚举。
答案 2 :(得分:18)
您只需添加属性:
[Newtonsoft.Json.Converters.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();
}
}
}
答案 3 :(得分:11)
这是一种简单的方法:
JsonConvert.SerializeObject(myObject, Formatting.Indented, new StringEnumConverter());
答案 4 :(得分:3)
我一直在使用一个非常好的解决方法,使用一个辅助私有属性进行序列化和反序列化,它可以通过枚举成员名称或EnumMemberAttribute
的值进行序列化。
我看到的最大优点是:
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); }
}
}
/* 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);
}
}
答案 5 :(得分:1)
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;}
答案 6 :(得分:0)
尝试使用
public enum Action {
[EnumMember(Value = "Remove")]
Remove=1,
[EnumMember(Value = "Add")]
Add=2
}
我不确定这是否适合你的情况,所以我可能错了。
答案 7 :(得分: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"));
}
答案 8 :(得分: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
答案 9 :(得分:0)
如果您使用 .Net 原生 json 序列化程序,即 System.Text.Json.Serialization,那么您可以在 enum 上添加一个属性,以便将 enum 转换为 string 而不是 int。
您应该将以下属性添加到您想要作为字符串的枚举中
<块引用>[JsonConverter(typeof(JsonStringEnumConverter))]