将枚举序列化为字符串

发布于 2025-01-02 09:28:30 字数 415 浏览 0 评论 0原文

我有一个枚举:

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)

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

I have an enum:

public enum Action {
    Remove=1,
    Add=2
}

And a class:

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

When serialize instance of Container to json I get: {Action:1} (in case Action is Remove).

I would like to get: {Action:Remove} (instead of int I need to ToString form of the enum)

Can I do it without adding another member to the class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

你在看孤独的风景 2025-01-09 09:28:30

您只需将属性:添加

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

You can just add the attribute:

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

to the enum property that is not serializing as a string.

or if you have a more exotic formatting in mind you could use the attributes as below to tell the JSON serializer to serialise only the property that you have formatted as you wish. Depends a bit on the rest of your implementation. It recognises the DataMember attribute on a property as well.

[JsonObject(MemberSerialization = MemberSerialization.OptOut)]
public class Container
{
    public Action Action { get; set; }

    [JsonProperty(PropertyName = "Action")]
    public string ActionString
    {
        get
        {
            return Action.ToString();
        }
    }
}
橪书 2025-01-09 09:28: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());

Using Json.Net, you can define a custom StringEnumConverter as

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

and serialize as

string json=JsonConvert.SerializeObject(container,new MyStringEnumConverter());
御守 2025-01-09 09:28:30

这是执行此操作的简单方法:

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

Here's a simple way to do this:

JsonConvert.SerializeObject(myObject, Formatting.Indented, new StringEnumConverter());
此刻的回忆 2025-01-09 09:28:30

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

来自 MSDN

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

  • 例如,如果您有 public enum Color {red, green, blue, Yellow,
    Pink}
    ,序列化黄色会产生数字 3 而不是字符串
    “黄色”。

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

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

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

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

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

The JSON formatter has very specialized behaviour when working with enumerations; the normal Data Contract attributes are ignored and it treats your enum as a number, not the more human-readable string you'd expect with other formats. Whilst this makes it easy to deal with flag-type enumerations, it makes most other types much harder to work with.

From MSDN:

Enumeration member values are treated as numbers in JSON, which is
different from how they are treated in data contracts, where they are
included as member names. For more information about the data contract
treatment, see Enumeration Types in Data Contracts.

  • For example, if you have public enum Color {red, green, blue, yellow,
    pink}
    , serializing yellow produces the number 3 and not the string
    "yellow".

  • All enum members are serializable. The EnumMemberAttribute and the
    NonSerializedAttribute attributes are ignored if used.

  • It is possible to deserialize a nonexistent enum value - for example,
    the value 87 can be deserialized into the previous Color enum even
    though there is no corresponding color name defined.

  • A flags enum is not special and is treated the same as any other enum.

The only practical way to resolve this, to allow end-users to specify a string instead of a number, is to not use the enum in your contract. Instead the practical answer is to replace your enum with a string and perform internal validation on the value such that it can be parsed into one of the valid enum representations.

Alternatively (though not for the feint of heart), you could replace the JSON formatter with your own, which would respect enumerations in the same way as other formatters.

欢你一世 2025-01-09 09:28:30

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

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

[JsonConverter(typeof(JsonStringEnumConverter))]

If you are using .Net native json serializer i.e. System.Text.Json.Serialization, then you can add an attribute on enum so that it converts enum to string and not int.

You should add following attributes to enum which you want as a string

[JsonConverter(typeof(JsonStringEnumConverter))]
涙—继续流 2025-01-09 09:28:30

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

我认为最大的优点是:

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

I've been using a very good workaround by using an auxiliary private property for serialization and deserialization that works either for serialization by the enum member name or by the value of the EnumMemberAttribute.

The greatest advantages I see, are that:

  • You don't need to tweak with the serializer
  • All the serialization logic is contained in the Data Object
  • You can hide your auxiliary property by setting it's accessibility modifier to private, since the DataContractSerializers are able to
    get and set private properties
  • You are able to serialize the enum as a string instead of an int

Your class will look like this:

[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);
    }
}
偷得浮生 2025-01-09 09:28:30

尝试使用

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

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

此处描述: http://msdn.microsoft.com/en-us/library /aa347875.aspx

Try using

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

I am not sure if this suits your case though, so I might be wrong.

It's described here: http://msdn.microsoft.com/en-us/library/aa347875.aspx

彩扇题诗 2025-01-09 09:28:30

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

The solution posted by Michal B works good. Here is another example.

You would need to do the Following as the Description Attribute is not serializable.

[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;}
眼角的笑意。 2025-01-09 09:28:30

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

容器定义

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

For serialization purpose, if the container must not contain enumeration properties but are filled with, you can use the extension method below.

Container definition

public class Container
{
    public string Action { get; set; }
}

Enumeration definition

public enum Action {
    Remove=1,
    Add=2
}

Code in views

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

Extension method

/// <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"));
}
梦幻的心爱 2025-01-09 09:28:30

我已经使用 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

I have put a solution to this using the Newtonsoft.Json library. It fixes the enum issue and also makes the error handling much better, and it works in IIS hosted services rather than self-hosted ones. It requires no changes or anything special to be added to your DataContract classes. It's quite a lot of code, so you can find it on GitHub here: https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

You have to add some entries to your Web.config to get it to work, you can see an example file here:
https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文