将枚举序列化为string
我有一个枚举:
public enum Action { Remove=1, Add=2 }
还有一堂课:
[DataContract] public class Container { [DataMember] public Action Action {get; set;} }
当序列化容器实例的JSON我得到: {Action:1}
(如果行动是删除)。
我想得到: {Action:Remove}
(而不是int我需要ToStringforms的枚举)
我能做到这一点吗?
使用枚举时,JSON格式化程序具有非常特殊的行为; 正常的数据合同属性将被忽略,它会将您的枚举视为一个数字,而不是您希望使用其他格式的更可读的string。 虽然这使得处理标志types枚举变得容易,但是使其他types的处理更加困难。
来自MSDN :
枚举成员值在JSON中被视为数字 ,这与数据合同中的处理方式不同,它们作为成员名称包含在数据合同中。 有关数据合同处理的更多信息,请参阅数据合同中的枚举types 。
例如,如果您有
public enum Color {red, green, blue, yellow, pink}
,则序列化黄色将生成数字3而不是string“黄色”。所有的枚举成员都是可序列化的。 如果使用EnumMemberAttribute和NonSerializedAttribute属性将被忽略。
可以反序列化不存在的枚举值 – 例如,即使没有定义相应的颜色名称,也可以将值87反序列化为以前的Color枚举。
一个标志枚举是不是特别的,并被视为任何其他枚举相同。
解决这个问题的唯一可行的方法是允许最终用户指定一个string而不是一个数字,就是不要在合约中使用枚举。 相反,实际的答案是用一个stringreplace你的枚举,并对该值执行内部validation,以便可以将其parsing为有效的枚举表示之一。
另外(尽pipe不是心的佯攻),你可以用自己的替代JSON格式化器,这将尊重枚举与其他格式化器相同的方式。
使用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());
你可以添加属性:
[Newtonsoft.Json.Converters.JsonConverter(typeof(StringEnumConverter))]
到不作为string序列化的枚举属性。
或者如果您有更奇特的格式,可以使用下面的属性来告诉JSON序列化程序仅序列化您已经格式化的属性。 取决于你的实现的其余部分。 它也识别属性上的DataMember属性。
[JsonObject(MemberSerialization = MemberSerialization.OptOut)] public class Container { public Action Action { get; set; } [JsonProperty(PropertyName = "Action")] public string ActionString { get { return Action.ToString(); } } }
这是一个简单的方法来做到这一点:
JsonConvert.SerializeObject(myObject, Formatting.Indented, new StringEnumConverter());
Michal B发布的解决scheme效果很好。 这是另一个例子。
由于说明属性不可序列化,因此您需要执行以下操作。
[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;}
我一直在使用一个非常好的解决方法,通过使用序列化和反序列化的辅助私有属性,用于通过枚举成员名称或EnumMemberAttribute
的值进行序列化。
我看到的最大的好处是:
- 你不需要调整序列化器
- 所有的序列化逻辑都包含在数据对象中
- 您可以通过将辅助属性设置为可访问性修饰符来隐藏辅助属性,因为DataContractSerializers能够获取和设置私有属性
- 您可以将枚举序列化为
string
而不是int
你的class级将如下所示:
[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); } }
尝试使用
public enum Action { [EnumMember(Value = "Remove")] Remove=1, [EnumMember(Value = "Add")] Add=2 }
我不确定这是否适合你的情况,所以我可能是错的。
这里描述: http : //msdn.microsoft.com/en-us/library/aa347875.aspx
为了序列化的目的,如果容器不能包含枚举属性但是被填充,你可以使用下面的扩展方法。
容器定义
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")); }
我已经把这个使用Newtonsoft.Json
库的解决scheme。 它修复了枚举问题,也使error handling更好,它在IIS托pipe服务,而不是自己托pipe的。 它不需要更改或任何特别添加到您的DataContract
类。 这是相当多的代码,所以你可以在这里find它在GitHub上: https : //github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs
你必须添加一些条目到你的Web.config
才能使它工作,你可以在这里看到一个示例文件: https : //github.com/jongrant/wcfjsonserializer/blob/master/Web.config