JavaScriptSerializer - 枚举作为字符串的 JSON 序列化

发布于 2024-08-25 14:18:57 字数 547 浏览 8 评论 0原文

我有一个包含 enum 属性的类,并且在使用 JavaScriptSerializer 序列化对象时,我的 json 结果包含枚举的整数值,而不是其 string< /代码>“名称”。有没有办法在我的 json 中以 string 形式获取枚举,而无需创建自定义 JavaScriptConverter?也许有一个属性可以用来装饰 enum 定义或对象属性?

举个例子:

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }
    Gender Gender { get; set; }
}

所需的 JSON 结果:

{ "Age": 35, "Gender": "Male" }

理想情况下寻找内置 .NET 框架类的答案,如果不可能,欢迎使用替代方案(如 Json.net)。

I have a class that contains an enum property, and upon serializing the object using JavaScriptSerializer, my json result contains the integer value of the enumeration rather than its string "name". Is there a way to get the enum as a string in my json without having to create a custom JavaScriptConverter? Perhaps there's an attribute that I could decorate the enum definition, or object property, with?

As an example:

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }
    Gender Gender { get; set; }
}

Desired JSON result:

{ "Age": 35, "Gender": "Male" }

Ideally looking for answer with built-in .NET framework classes, if not possible alternatives (like Json.net) are welcome.

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

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

发布评论

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

评论(30

千柳 2024-09-01 14:18:58

不确定这是否仍然相关,但我必须直接写入 json 文件,我想出了以下将几个 stackoverflow 答案拼凑在一起的结果,

public class LowercaseJsonSerializer
{
    private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
    {
        ContractResolver = new LowercaseContractResolver()
    };

    public static void Serialize(TextWriter file, object o)
    {
        JsonSerializer serializer = new JsonSerializer()
        {
            ContractResolver = new LowercaseContractResolver(),
            Formatting = Formatting.Indented,
            NullValueHandling = NullValueHandling.Ignore
        };
        serializer.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
        serializer.Serialize(file, o);
    }

    public class LowercaseContractResolver : DefaultContractResolver
    {
        protected override string ResolvePropertyName(string propertyName)
        {
            return Char.ToLowerInvariant(propertyName[0]) + propertyName.Substring(1);
        }
    }
}

它确保我的所有 json 键都是小写的,根据 json“规则”开始。将其格式化为干净的缩进并忽略输出中的空值。同样,通过添加 StringEnumConverter,它可以打印枚举及其字符串值。

就我个人而言,我发现这是我能想到的最干净的方法,而不必用注释弄脏模型。

用法:

    internal void SaveJson(string fileName)
    {
        // serialize JSON directly to a file
        using (StreamWriter file = File.CreateText(@fileName))
        {
            LowercaseJsonSerializer.Serialize(file, jsonobject);
        }
    }

Not sure if this is still relevant but I had to write straight to a json file and I came up with the following piecing several stackoverflow answers together

public class LowercaseJsonSerializer
{
    private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
    {
        ContractResolver = new LowercaseContractResolver()
    };

    public static void Serialize(TextWriter file, object o)
    {
        JsonSerializer serializer = new JsonSerializer()
        {
            ContractResolver = new LowercaseContractResolver(),
            Formatting = Formatting.Indented,
            NullValueHandling = NullValueHandling.Ignore
        };
        serializer.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
        serializer.Serialize(file, o);
    }

    public class LowercaseContractResolver : DefaultContractResolver
    {
        protected override string ResolvePropertyName(string propertyName)
        {
            return Char.ToLowerInvariant(propertyName[0]) + propertyName.Substring(1);
        }
    }
}

It assures all my json keys are lowercase starting according to json "rules". Formats it cleanly indented and ignores nulls in the output. Aslo by adding a StringEnumConverter it prints enums with their string value.

Personally I find this the cleanest I could come up with, without having to dirty the model with annotations.

usage:

    internal void SaveJson(string fileName)
    {
        // serialize JSON directly to a file
        using (StreamWriter file = File.CreateText(@fileName))
        {
            LowercaseJsonSerializer.Serialize(file, jsonobject);
        }
    }
吖咩 2024-09-01 14:18:58

对于 VB.net,我发现了以下作品:

Dim sec = New Newtonsoft.Json.Converters.StringEnumConverter()
sec.NamingStrategy() = New Serialization.CamelCaseNamingStrategy

Dim JSON_s As New JsonSerializer
JSON_s.Converters.Add(sec)

Dim jsonObject As JObject
jsonObject = JObject.FromObject(SomeObject, JSON_s)
Dim text = jsonObject.ToString

IO.File.WriteAllText(filePath, text)

And for VB.net I found the following works:

Dim sec = New Newtonsoft.Json.Converters.StringEnumConverter()
sec.NamingStrategy() = New Serialization.CamelCaseNamingStrategy

Dim JSON_s As New JsonSerializer
JSON_s.Converters.Add(sec)

Dim jsonObject As JObject
jsonObject = JObject.FromObject(SomeObject, JSON_s)
Dim text = jsonObject.ToString

IO.File.WriteAllText(filePath, text)
是伱的 2024-09-01 14:18:58

我使用 Newtonsoft.Json 库将该解决方案的所有部分组合在一起。它修复了枚举问题,并且还使错误处理变得更好,并且它适用于 IIS 托管服务。代码相当多,因此您可以在 GitHub 上找到它:https:// /github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

您必须向 Web.config 添加一些条目才能使其正常工作,您可以查看示例文件这里:
https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config

I have put together all of the pieces of this solution 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. 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

一曲琵琶半遮面シ 2024-09-01 14:18:58

对于任何在 22 年 5 月需要 .NET 6 解决方案并且仍在使用 Newtonsoft 的人,您可以像这样全局注册转换器:

var builder = WebApplication.CreateBuilder(args);
...
builder.Services.AddControllers(options => options.RespectBrowserAcceptHeader = true)
.AddNewtonsoftJson(opt =>
{
    opt.SerializerSettings.ContractResolver = new DefaultContractResolver();
    opt.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
})
.AddXmlSerializerFormatters()
.AddXmlDataContractSerializerFormatters();

For anyone needing a solution in May of '22 for .NET 6 and still using Newtonsoft, you can register the converter globally like this:

var builder = WebApplication.CreateBuilder(args);
...
builder.Services.AddControllers(options => options.RespectBrowserAcceptHeader = true)
.AddNewtonsoftJson(opt =>
{
    opt.SerializerSettings.ContractResolver = new DefaultContractResolver();
    opt.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
})
.AddXmlSerializerFormatters()
.AddXmlDataContractSerializerFormatters();
苦妄 2024-09-01 14:18:58
        Person p = new Person();
        p.Age = 35;
        p.Gender = Gender.Male;
        //1.  male="Male";
        string male = Gender.Male.ToString();

        p.Gender = Gender.Female;

        //2.  female="Female";
        string female = Enum.GetName(typeof(Gender), p.Gender);

        JObject jobj = new JObject();
        jobj["Age"] = p.Age;
        jobj["Gender"] = male;
        jobj["Gender2"] = female;

        //you result:  josn= {"Age": 35,"Gender": "Male","Gender2": "Female"}
        string json = jobj.ToString();
        Person p = new Person();
        p.Age = 35;
        p.Gender = Gender.Male;
        //1.  male="Male";
        string male = Gender.Male.ToString();

        p.Gender = Gender.Female;

        //2.  female="Female";
        string female = Enum.GetName(typeof(Gender), p.Gender);

        JObject jobj = new JObject();
        jobj["Age"] = p.Age;
        jobj["Gender"] = male;
        jobj["Gender2"] = female;

        //you result:  josn= {"Age": 35,"Gender": "Male","Gender2": "Female"}
        string json = jobj.ToString();
黑凤梨 2024-09-01 14:18:57

我发现 Json.NET 提供了我正在寻找的确切功能一个 JsonConverter 属性,传入内置 StringEnumConverter 类型:

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

[JsonConverter(typeof(StringEnumConverter))]
public Gender Gender { get; set; }

更多详细信息,请访问 StringEnumConverter 文档

还有其他地方可以更全局地配置此转换器:

  • 枚举本身,如果您希望枚举始终被序列化/反序列化为字符串:

     [JsonConverter(typeof(StringEnumConverter))]  
      枚举性别 { 男、女 }
    
  • 如果有人想要避免属性修饰,您可以将转换器添加到 JsonSerializer(由 比约恩·埃吉尔):

     serializer.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); 
    

它将适用于序列化过程中看到的每个枚举(由 Travis 建议)。

  • 或 JsonConverter(由 banana 建议):

     JsonConvert.SerializeObject(MyObject, 
          新 Newtonsoft.Json.Converters.StringEnumConverter());
    

此外,您可以使用 StringEnumConverter(NamingStrategy, Boolean) 构造函数。

I have found that Json.NET provides the exact functionality I'm looking for with a JsonConverter attribute, passing in the built-in StringEnumConverter type:

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

[JsonConverter(typeof(StringEnumConverter))]
public Gender Gender { get; set; }

More details at available on StringEnumConverter documentation.

There are other places to configure this converter more globally:

  • enum itself if you want enum always be serialized/deserialized as string:

      [JsonConverter(typeof(StringEnumConverter))]  
      enum Gender { Male, Female }
    
  • In case anyone wants to avoid attribute decoration, you can add the converter to your JsonSerializer (suggested by Bjørn Egil):

      serializer.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); 
    

and it will work for every enum it sees during that serialization (suggested by Travis).

  • or JsonConverter (suggested by banana):

      JsonConvert.SerializeObject(MyObject, 
          new Newtonsoft.Json.Converters.StringEnumConverter());
    

Additionally you can control casing and whether numbers are still accepted by using StringEnumConverter(NamingStrategy, Boolean) constructor.

梦里兽 2024-09-01 14:18:57

不,没有您可以使用的特殊属性。 JavaScriptSerializer 将枚举序列化为其数值而不是字符串表示形式。您需要使用自定义序列化将enum序列化为其名称而不是数值。


如果您可以使用 JSON.Net 而不是 JavaScriptSerializer,请参阅 对此问题的回答 < a href="https://stackoverflow.com/users/56829/omer-bokhari">Omer Bokhari:JSON.net 涵盖了此用例(通过属性 [JsonConverter(typeof(StringEnumConverter))) ])以及许多其他未由内置 .net 序列化程序处理的内容。 这里是比较序列化器特性和功能的链接

No there is no special attribute you can use. JavaScriptSerializer serializes enums to their numeric values and not their string representation. You would need to use custom serialization to serialize the enum as its name instead of numeric value.


If you can use JSON.Net instead of JavaScriptSerializer than see answer on this question provided by Omer Bokhari: JSON.net covers this use case (via the attribute [JsonConverter(typeof(StringEnumConverter))]) and many others not handled by the built in .net serializers. Here is a link comparing features and functionalities of the serializers.

请你别敷衍 2024-09-01 14:18:57

@Iggy 答案将 c# 枚举的 JSON 序列化设置为仅适用于 ASP.NET(Web API 等)的字符串。

但要使其也适用于临时序列化,请将以下内容添加到您的启动类(例如 Global.asax Application_Start)

//convert Enums to Strings (instead of Integer) globally
JsonConvert.DefaultSettings = (() =>
{
    var settings = new JsonSerializerSettings();
    settings.Converters.Add(new StringEnumConverter { CamelCaseText = true });
    return settings;
});

更多信息 在 Json.NET 页面

此外,要让您的枚举成员序列化到特定文本/反序列化到特定文本,请使用

System.Runtime.Serialization.EnumMember

属性,如下所示:

public enum time_zone_enum
{
    [EnumMember(Value = "Europe/London")] 
    EuropeLondon,

    [EnumMember(Value = "US/Alaska")] 
    USAlaska
}

@Iggy answer sets JSON serialization of c# enum as string only for ASP.NET (Web API and so).

But to make it work also with ad hoc serialization, add following to your start class (like Global.asax Application_Start)

//convert Enums to Strings (instead of Integer) globally
JsonConvert.DefaultSettings = (() =>
{
    var settings = new JsonSerializerSettings();
    settings.Converters.Add(new StringEnumConverter { CamelCaseText = true });
    return settings;
});

More information on the Json.NET page

Additionally, to have your enum member to serialize/deserialize to/from specific text, use the

System.Runtime.Serialization.EnumMember

attribute, like this:

public enum time_zone_enum
{
    [EnumMember(Value = "Europe/London")] 
    EuropeLondon,

    [EnumMember(Value = "US/Alaska")] 
    USAlaska
}
绅刃 2024-09-01 14:18:57

将以下内容添加到您的 global.asax 中,以将 C# 枚举的 JSON 序列化为字符串

  HttpConfiguration config = GlobalConfiguration.Configuration;
            config.Formatters.JsonFormatter.SerializerSettings.Formatting =
                Newtonsoft.Json.Formatting.Indented;

            config.Formatters.JsonFormatter.SerializerSettings.Converters.Add
                (new Newtonsoft.Json.Converters.StringEnumConverter());

Add the below to your global.asax for JSON serialization of c# enum as string

  HttpConfiguration config = GlobalConfiguration.Configuration;
            config.Formatters.JsonFormatter.SerializerSettings.Formatting =
                Newtonsoft.Json.Formatting.Indented;

            config.Formatters.JsonFormatter.SerializerSettings.Converters.Add
                (new Newtonsoft.Json.Converters.StringEnumConverter());
静若繁花 2024-09-01 14:18:57

在 .net core 3 中,现在可以通过 System.Text.Json 中的内置类实现这一点(编辑:System.Text.Json 也可以作为 .net core 的 NuGet 包提供2.0 和 .net Framework 4.7.2 及更高版本,根据 docs):

var person = new Person();
// Create and add a converter which will use the string representation instead of the numeric value.
var stringEnumConverter = new System.Text.Json.Serialization.JsonStringEnumConverter();
JsonSerializerOptions opts = new JsonSerializerOptions();
opts.Converters.Add(stringEnumConverter);
// Generate json string.
var json = JsonSerializer.Serialize<Person>(person, opts);

使用特定属性的属性装饰来配置 JsonStringEnumConverter

using System.Text.Json.Serialization;

[JsonConverter(typeof(JsonStringEnumConverter))]
public Gender Gender { get; set; }

如果您希望始终将枚举转换为字符串,请将属性放在枚举本身。

[JsonConverter(typeof(JsonStringEnumConverter))] 
enum Gender { Male, Female }

In .net core 3 this is now possible with the built-in classes in System.Text.Json (edit: System.Text.Json is also available as a NuGet package for .net core 2.0 and .net framework 4.7.2 and later versions according to the docs):

var person = new Person();
// Create and add a converter which will use the string representation instead of the numeric value.
var stringEnumConverter = new System.Text.Json.Serialization.JsonStringEnumConverter();
JsonSerializerOptions opts = new JsonSerializerOptions();
opts.Converters.Add(stringEnumConverter);
// Generate json string.
var json = JsonSerializer.Serialize<Person>(person, opts);

To configure JsonStringEnumConverter with attribute decoration for the specific property:

using System.Text.Json.Serialization;

[JsonConverter(typeof(JsonStringEnumConverter))]
public Gender Gender { get; set; }

If you want to always convert the enum as string, put the attribute at the enum itself.

[JsonConverter(typeof(JsonStringEnumConverter))] 
enum Gender { Male, Female }
夏日浅笑〃 2024-09-01 14:18:57

我无法像(@ob.)的最佳答案中那样更改源模型,并且我不想像@Iggy 那样在全球范围内注册它。所以我结合了 https://stackoverflow.com/a/2870420/237091 和 @Iggy 的 https://stackoverflow.com/a/18152942/237091 允许在 SerializeObject 命令本身期间设置字符串枚举转换器:

Newtonsoft.Json.JsonConvert.SerializeObject(
    objectToSerialize, 
    Newtonsoft.Json.Formatting.None, 
    new Newtonsoft.Json.JsonSerializerSettings()
    {
        Converters = new List<Newtonsoft.Json.JsonConverter> {
            new Newtonsoft.Json.Converters.StringEnumConverter()
        }
    })

I wasn't able to change the source model like in the top answer (of @ob.), and I didn't want to register it globally like @Iggy. So I combined https://stackoverflow.com/a/2870420/237091 and @Iggy's https://stackoverflow.com/a/18152942/237091 to allow setting up the string enum converter on during the SerializeObject command itself:

Newtonsoft.Json.JsonConvert.SerializeObject(
    objectToSerialize, 
    Newtonsoft.Json.Formatting.None, 
    new Newtonsoft.Json.JsonSerializerSettings()
    {
        Converters = new List<Newtonsoft.Json.JsonConverter> {
            new Newtonsoft.Json.Converters.StringEnumConverter()
        }
    })
浅忆流年 2024-09-01 14:18:57

Omer Bokhari 和 uri 的答案的组合始终是我的解决方案,因为我想要提供的值通常与我的枚举中的值不同,特别是如果需要,我希望能够更改我的枚举。

所以如果有人感兴趣的话,它是这样的:

public enum Gender
{
   [EnumMember(Value = "male")] 
   Male,
   [EnumMember(Value = "female")] 
   Female
}

class Person
{
    int Age { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    Gender Gender { get; set; }
}

The combination of Omer Bokhari and uri 's answers is alsways my solution since the values that I want to provide is usually different from what I have in my enum specially that I would like to be able to change my enums if I need to.

So if anyone is interested, it is something like this:

public enum Gender
{
   [EnumMember(Value = "male")] 
   Male,
   [EnumMember(Value = "female")] 
   Female
}

class Person
{
    int Age { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    Gender Gender { get; set; }
}
违心° 2024-09-01 14:18:57

通过添加 ScriptIgnore 属性添加到 Gender 属性,导致其无法序列化,并添加一个 GenderString 属性,确实被序列化:

class Person
{
    int Age { get; set; }

    [ScriptIgnore]
    Gender Gender { get; set; }

    string GenderString { get { return Gender.ToString(); } }
}

This is easily done by adding a ScriptIgnore attribute to the Gender property, causing it to not be serialised, and adding a GenderString property which does get serialised:

class Person
{
    int Age { get; set; }

    [ScriptIgnore]
    Gender Gender { get; set; }

    string GenderString { get { return Gender.ToString(); } }
}
半岛未凉 2024-09-01 14:18:57

ASP.NET Core方式:

public class Startup
{
  public IServiceProvider ConfigureServices(IServiceCollection services)
  {
    services.AddMvc().AddJsonOptions(options =>
    {
      options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
    });
  }
}

https://gist.github.com/regisdiogo/27f62ef83a804668eb0d9d0f63989e3e

ASP.NET Core way:

public class Startup
{
  public IServiceProvider ConfigureServices(IServiceCollection services)
  {
    services.AddMvc().AddJsonOptions(options =>
    {
      options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
    });
  }
}

https://gist.github.com/regisdiogo/27f62ef83a804668eb0d9d0f63989e3e

挽清梦 2024-09-01 14:18:57

此版本的 Stephen 的答案不会更改 JSON 中的名称:

[DataContract(
    Namespace = 
       "http://schemas.datacontract.org/2004/07/Whatever")]
class Person
{
    [DataMember]
    int Age { get; set; }

    Gender Gender { get; set; }

    [DataMember(Name = "Gender")]
    string GenderString
    {
        get { return this.Gender.ToString(); }
        set 
        { 
            Gender g; 
            this.Gender = Enum.TryParse(value, true, out g) ? g : Gender.Male; 
        }
    }
}

This version of Stephen's answer doesn't change the name in the JSON:

[DataContract(
    Namespace = 
       "http://schemas.datacontract.org/2004/07/Whatever")]
class Person
{
    [DataMember]
    int Age { get; set; }

    Gender Gender { get; set; }

    [DataMember(Name = "Gender")]
    string GenderString
    {
        get { return this.Gender.ToString(); }
        set 
        { 
            Gender g; 
            this.Gender = Enum.TryParse(value, true, out g) ? g : Gender.Male; 
        }
    }
}
み青杉依旧 2024-09-01 14:18:57

这是 newtonsoft.json 的答案

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }

    [JsonConverter(typeof(StringEnumConverter))]
    Gender Gender { get; set; }
}

Here is the answer for newtonsoft.json

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }

    [JsonConverter(typeof(StringEnumConverter))]
    Gender Gender { get; set; }
}
残花月 2024-09-01 14:18:57

Asp.Net Core 3 与 System.Text.Json

public void ConfigureServices(IServiceCollection services)
{

    services
        .AddControllers()
        .AddJsonOptions(options => 
           options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())
        );

    //...
 }

Asp.Net Core 3 with System.Text.Json

public void ConfigureServices(IServiceCollection services)
{

    services
        .AddControllers()
        .AddJsonOptions(options => 
           options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())
        );

    //...
 }
爱已欠费 2024-09-01 14:18:57

对于 .NET 6.0 如果你想使用内置的 JsonSerializer (System.Text.Json)

那么,它是开箱即用的,你只需要使用内置的 JsonStringEnumConverter 属性。例如:

[JsonConverter(typeof(JsonStringEnumConverter))]
public SomeEnumType EnumProperty { get; set; }

就是这样,但请确保您的 SomeEnumType 包含具有确切字符串值的值,否则它将引发异常。外壳似乎不敏感。

参考:

For .NET 6.0 if you want to use the built-in JsonSerializer (System.Text.Json)

Then, it comes out-of-the-box, you just need to use the built-in JsonStringEnumConverter attribute. For example:

[JsonConverter(typeof(JsonStringEnumConverter))]
public SomeEnumType EnumProperty { get; set; }

And that's it, BUT make sure your SomeEnumType contains values with the exact string values, otherwise it will throw an exception. Casing seems to be insensitive.

Reference: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-customize-properties?pivots=dotnet-6-0#enums-as-strings

盛装女皇 2024-09-01 14:18:57

如果您不想使用 JsonConverter 属性,您还可以向您的 JsonSerializer 添加转换器:

string SerializedResponse = JsonConvert.SerializeObject(
     objToSerialize, 
     new Newtonsoft.Json.Converters.StringEnumConverter()
); 

它将适用于它在过程中看到的每个 enum那个序列化。

You can also add a converter to your JsonSerializer if you don't want to use JsonConverter attribute:

string SerializedResponse = JsonConvert.SerializeObject(
     objToSerialize, 
     new Newtonsoft.Json.Converters.StringEnumConverter()
); 

It will work for every enum it sees during that serialization.

弱骨蛰伏 2024-09-01 14:18:57

对于 ASP.Net core 只需将以下内容添加到您的启动类中:

JsonConvert.DefaultSettings = (() =>
        {
            var settings = new JsonSerializerSettings();
            settings.Converters.Add(new StringEnumConverter { AllowIntegerValues = false });
            return settings;
        });

For ASP.Net core Just add the following to your Startup Class:

JsonConvert.DefaultSettings = (() =>
        {
            var settings = new JsonSerializerSettings();
            settings.Converters.Add(new StringEnumConverter { AllowIntegerValues = false });
            return settings;
        });
能怎样 2024-09-01 14:18:57

下面是一个简单的解决方案,它将服务器端 C# 枚举序列化为 JSON,并使用结果填充客户端

我已经包含了端到端解决方案,因为我认为大多数想要将 C# 枚举序列化为 JSON 的人也可能会使用它来填充

这里是:

示例枚举

public enum Role
{
    None = Permission.None,
    Guest = Permission.Browse,
    Reader = Permission.Browse| Permission.Help ,
    Manager = Permission.Browse | Permission.Help | Permission.Customise
}

一个使用按位或来生成权限系统的复杂枚举。因此,您不能依赖简单的索引 [0,1,2..] 来获取枚举的整数值。

服务器端 - C#

Get["/roles"] = _ =>
{
    var type = typeof(Role);
    var data = Enum
        .GetNames(type)
        .Select(name => new 
            {
                Id = (int)Enum.Parse(type, name), 
                Name = name 
            })
        .ToArray();

    return Response.AsJson(data);
};

上面的代码使用 NancyFX 框架来处理 Get 请求。它使用 Nancy 的 Response.AsJson() 辅助方法 - 但不用担心,您可以使用任何标准 JSON 格式化程序,因为枚举已经被投影到准备序列化的简单匿名类型中。

生成的 JSON

[
    {"Id":0,"Name":"None"},
    {"Id":2097155,"Name":"Guest"},
    {"Id":2916367,"Name":"Reader"},
    {"Id":4186095,"Name":"Manager"}
]

客户端 - CoffeeScript

fillSelect=(id, url, selectedValue=0)->
    $select = $ id
    $option = (item)-> $ "<option/>", 
        {
            value:"#{item.Id}"
            html:"#{item.Name}"
            selected:"selected" if item.Id is selectedValue
        }
    $.getJSON(url).done (data)->$option(item).appendTo $select for item in data

$ ->
    fillSelect "#role", "/roles", 2916367

HTML 之前

<select id="role" name="role"></select>

HTML 之后

<select id="role" name="role">
    <option value="0">None</option>
    <option value="2097155">Guest</option>
    <option value="2916367" selected="selected">Reader</option>
    <option value="4186095">Manager</option>
</select>

Here is a simple solution that serializes a server-side C# enum to JSON and uses the result to populate a client-side <select> element. This works for both simple enums and bitflag enums.

I have included the end-to-end solution because I think most people wanting to serialize a C# enum to JSON will also probably be using it to fill a <select> drop-down.

Here goes:

Example Enum

public enum Role
{
    None = Permission.None,
    Guest = Permission.Browse,
    Reader = Permission.Browse| Permission.Help ,
    Manager = Permission.Browse | Permission.Help | Permission.Customise
}

A complex enum that uses bitwise ORs to generate a permissions system. So you can't rely on the simple index [0,1,2..] for the integer value of the enum.

Server Side - C#

Get["/roles"] = _ =>
{
    var type = typeof(Role);
    var data = Enum
        .GetNames(type)
        .Select(name => new 
            {
                Id = (int)Enum.Parse(type, name), 
                Name = name 
            })
        .ToArray();

    return Response.AsJson(data);
};

The code above uses the NancyFX framework to handle the Get request. It uses Nancy's Response.AsJson() helper method - but don't worry, you can use any standard JSON formatter as the enum has already been projected into a simple anonymous type ready for serialization.

Generated JSON

[
    {"Id":0,"Name":"None"},
    {"Id":2097155,"Name":"Guest"},
    {"Id":2916367,"Name":"Reader"},
    {"Id":4186095,"Name":"Manager"}
]

Client Side - CoffeeScript

fillSelect=(id, url, selectedValue=0)->
    $select = $ id
    $option = (item)-> $ "<option/>", 
        {
            value:"#{item.Id}"
            html:"#{item.Name}"
            selected:"selected" if item.Id is selectedValue
        }
    $.getJSON(url).done (data)->$option(item).appendTo $select for item in data

$ ->
    fillSelect "#role", "/roles", 2916367

HTML Before

<select id="role" name="role"></select>

HTML After

<select id="role" name="role">
    <option value="0">None</option>
    <option value="2097155">Guest</option>
    <option value="2916367" selected="selected">Reader</option>
    <option value="4186095">Manager</option>
</select>
聊慰 2024-09-01 14:18:57

请注意,当存在 Description 属性时,序列化没有答案。

这是我支持 Description 属性的实现。

public class CustomStringEnumConverter : Newtonsoft.Json.Converters.StringEnumConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Type type = value.GetType() as Type;

        if (!type.IsEnum) throw new InvalidOperationException("Only type Enum is supported");
        foreach (var field in type.GetFields())
        {
            if (field.Name == value.ToString())
            {
                var attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
                writer.WriteValue(attribute != null ? attribute.Description : field.Name);

                return;
            }
        }

        throw new ArgumentException("Enum not found");
    }
}

枚举:

public enum FooEnum
{
    // Will be serialized as "Not Applicable"
    [Description("Not Applicable")]
    NotApplicable,

    // Will be serialized as "Applicable"
    Applicable
}

用法:

[JsonConverter(typeof(CustomStringEnumConverter))]
public FooEnum test { get; set; }

Noticed that there is no answer for serialization when there is a Description attribute.

Here is my implementation that supports the Description attribute.

public class CustomStringEnumConverter : Newtonsoft.Json.Converters.StringEnumConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Type type = value.GetType() as Type;

        if (!type.IsEnum) throw new InvalidOperationException("Only type Enum is supported");
        foreach (var field in type.GetFields())
        {
            if (field.Name == value.ToString())
            {
                var attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
                writer.WriteValue(attribute != null ? attribute.Description : field.Name);

                return;
            }
        }

        throw new ArgumentException("Enum not found");
    }
}

Enum:

public enum FooEnum
{
    // Will be serialized as "Not Applicable"
    [Description("Not Applicable")]
    NotApplicable,

    // Will be serialized as "Applicable"
    Applicable
}

Usage:

[JsonConverter(typeof(CustomStringEnumConverter))]
public FooEnum test { get; set; }
左岸枫 2024-09-01 14:18:57

您可以通过调用 JsonConverter.SerializeObject 创建 JsonSerializerSettings,如下所示:

var result = JsonConvert.SerializeObject
            (
                dataObject,
                new JsonSerializerSettings
                {
                    Converters = new [] {new StringEnumConverter()}
                }
            );

You can create JsonSerializerSettings with the call to JsonConverter.SerializeObject as below:

var result = JsonConvert.SerializeObject
            (
                dataObject,
                new JsonSerializerSettings
                {
                    Converters = new [] {new StringEnumConverter()}
                }
            );
地狱即天堂 2024-09-01 14:18:57

对于.Net Core:-

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddJsonFormatters(f => f.Converters.Add(new StringEnumConverter()));
    ...
}

For .Net Core :-

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddJsonFormatters(f => f.Converters.Add(new StringEnumConverter()));
    ...
}
夜还是长夜 2024-09-01 14:18:57

使用这个:

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

[Serializable]
[JsonConverter(typeof(StringEnumConverter))]
public enum Gender { Male, Female }

Use this:

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

[Serializable]
[JsonConverter(typeof(StringEnumConverter))]
public enum Gender { Male, Female }
怪我鬧 2024-09-01 14:18:57

以防万一有人发现上述内容不够,我最终解决了这个超载问题:

JsonConvert.SerializeObject(objToSerialize, Formatting.Indented, new Newtonsoft.Json.Converters.StringEnumConverter())

Just in case anybody finds the above insufficient, I ended up settling with this overload:

JsonConvert.SerializeObject(objToSerialize, Formatting.Indented, new Newtonsoft.Json.Converters.StringEnumConverter())
想你的星星会说话 2024-09-01 14:18:57

这是一个老问题,但我想我会贡献以防万一。在我的项目中,我对任何 Json 请求使用单独的模型。模型通常与带有“Json”前缀的域对象具有相同的名称。模型使用 AutoMapper 进行映射。通过让 json 模型声明一个作为域类枚举的字符串属性,AutoMapper 将解析为它的字符串表示形式。

如果您想知道,我需要 Json 序列化类的单独模型,因为内置序列化器会出现循环引用。

希望这对某人有帮助。

This is an old question but I thought I'd contribute just in case. In my projects I use separate models for any Json requests. A model would typically have same name as domain object with "Json" prefix. Models are mapped using AutoMapper. By having the json model declare a string property that is an enum on domain class, AutoMapper will resolve to it's string presentation.

In case you are wondering, I need separate models for Json serialized classes because inbuilt serializer comes up with circular references otherwise.

Hope this helps someone.

感情旳空白 2024-09-01 14:18:57

命名空间 System.Text.Json.Serialization 有 JsonStringEnumConverter ,可以像下面这样使用。
[JsonConverter(typeof(JsonStringEnumConverter))]

The namespace System.Text.Json.Serialization has JsonStringEnumConverter which can be used like below.
[JsonConverter(typeof(JsonStringEnumConverter))]

飘过的浮云 2024-09-01 14:18:57

实际上,您可以使用 JavaScriptConverter 通过内置的 JavaScriptSerializer 来完成此操作。通过将枚举转换为 Uri,您可以将其编码为字符串。

我已经描述了如何对日期执行此操作,但它也可以用于枚举。
.NET JavaScriptSerializer 的自定义日期时间 JSON 格式

You can actually use a JavaScriptConverter to accomplish this with the built-in JavaScriptSerializer. By converting your enum to a Uri you can encode it as a string.

I've described how to do this for dates but it can be used for enums as well.
Custom DateTime JSON Format for .NET JavaScriptSerializer.

荒路情人 2024-09-01 14:18:57

稍微更面向未来的选项

面对同样的问题,我们确定需要一个自定义版本的 StringEnumConverter 来确保我们的枚举值可以随着时间的推移而扩展,而不会在反序列化方面发生灾难性的破坏(请参阅背景以下)。即使有效负载包含没有命名定义的枚举值,使用下面的 SafeEnumConverter 也可以完成反序列化,这更接近 int 到枚举转换的工作方式。

用法:

[SafeEnumConverter]
public enum Colors
{
    Red,
    Green,
    Blue,
    Unsupported = -1
}

[SafeEnumConverter((int) Colors.Blue)]
public enum Colors
{
    Red,
    Green,
    Blue
}

来源:

public class SafeEnumConverter : StringEnumConverter
{
    private readonly int _defaultValue;

    public SafeEnumConverter()
    {
        // if you've been careful to *always* create enums with `0` reserved
        // as an unknown/default value (which you should), you could use 0 here. 
        _defaultValue = -1;
    }

    public SafeEnumConverter(int defaultValue)
    {
        _defaultValue = defaultValue;
    }

    /// <summary>
    /// Reads the provided JSON and attempts to convert using StringEnumConverter. If that fails set the value to the default value.
    /// </summary>
    /// <returns>The deserialized value of the enum if it exists or the default value if it does not.</returns>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            return base.ReadJson(reader, objectType, existingValue, serializer);
        }
        catch
        {
            return Enum.Parse(objectType, $"{_defaultValue}");
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return base.CanConvert(objectType) && objectType.GetTypeInfo().IsEnum;
    }
}

背景

当我们考虑使用 StringEnumConverter 时,我们遇到的问题是,在添加新枚举值时,我们还需要被动,但并非每个客户端都能立即意识到新的价值。在这些情况下,使用 Newtonsoft JSON 打包的 StringEnumConverter 会抛出类似于“Error conversion value SomeString to type EnumType”的 JsonSerializationException,然后进行whole 反序列化进程失败。这对我们来说是一个大问题,因为即使客户端计划忽略/丢弃它不理解的属性值,它仍然需要能够反序列化其余的有效负载!

A slightly more future-proof option

Facing the same question, we determined that we needed a custom version of StringEnumConverter to make sure that our enum values could expand over time without breaking catastrophically on the deserializing side (see background below). Using the SafeEnumConverter below allows deserialization to finish even if the payload contains a value for the enum that does not have a named definition, closer to how int-to-enum conversion would work.

Usage:

[SafeEnumConverter]
public enum Colors
{
    Red,
    Green,
    Blue,
    Unsupported = -1
}

or

[SafeEnumConverter((int) Colors.Blue)]
public enum Colors
{
    Red,
    Green,
    Blue
}

Source:

public class SafeEnumConverter : StringEnumConverter
{
    private readonly int _defaultValue;

    public SafeEnumConverter()
    {
        // if you've been careful to *always* create enums with `0` reserved
        // as an unknown/default value (which you should), you could use 0 here. 
        _defaultValue = -1;
    }

    public SafeEnumConverter(int defaultValue)
    {
        _defaultValue = defaultValue;
    }

    /// <summary>
    /// Reads the provided JSON and attempts to convert using StringEnumConverter. If that fails set the value to the default value.
    /// </summary>
    /// <returns>The deserialized value of the enum if it exists or the default value if it does not.</returns>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            return base.ReadJson(reader, objectType, existingValue, serializer);
        }
        catch
        {
            return Enum.Parse(objectType, $"{_defaultValue}");
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return base.CanConvert(objectType) && objectType.GetTypeInfo().IsEnum;
    }
}

Background

When we looked at using the StringEnumConverter, the problem we had is that we also needed passivity for cases when a new enum value was added, but not every client was immediately aware of the new value. In these cases, the StringEnumConverter packaged with Newtonsoft JSON throws a JsonSerializationException similar to "Error converting value SomeString to type EnumType" and then the whole deserialization process fails. This was a deal breaker for us, because even if the client planned on ignoring/discarding the property value that it didn't understand, it still needed to be capable of deserializing the rest of the payload!

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