将枚举序列化为字符串

时间:2012-02-06 07:21:56

标签: c# json datacontract

我有一个枚举:

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形式的枚举)

我可以不在课程中添加其他成员吗?

10 个答案:

答案 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的值进行序列化。

我看到的最大优点是:

  • 您无需使用序列化程序进行调整
  • 所有序列化逻辑都包含在数据对象
  • 您可以通过将辅助属性设置为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);
    }
}

答案 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
}

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

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

答案 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))]