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

1420

我有一个包含enum属性的类,当我使用JavaScriptSerializer对该对象进行序列化时,我的JSON结果包含了枚举的整数值而不是其字符串“名称”。有没有办法在我的JSON中获取枚举作为string,而无需创建自定义的JavaScriptConverter?也许有一种属性可以装饰enum定义或对象属性吗?

例如:

enum Gender { Male, Female }

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

期望的 JSON 结果:

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

最好使用内置的 .NET Framework 类来回答,如果不可能,欢迎使用替代方案(例如 Json.net)。


10
改变成什么?最受点赞的回答实际上并没有回答这个问题——是的,在其他情境下它非常有用,因此得到了投票,但如果你被限制使用MS JavaScriptSerializer,它对你来说就没有任何实际用途,尤其是在使用页面方法时以及按照问题所要求的情况下。被接受的答案说这是不可能的。我的回答虽然有点取巧,但可以完成任务。 - Stephen Kennedy
31个回答

2400

我发现 Json.NET 提供了我需要的精确功能,使用 JsonConverter 属性,并传入内置的 StringEnumConverter 类型:

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

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

更多细节请参阅StringEnumConverter文档

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

  • 如果您希望枚举始终被序列化/反序列化为字符串,可以使用以下代码:

      [JsonConverter(typeof(StringEnumConverter))]  
      enum Gender { Male, Female }
    
  • 如果有人想避免属性装饰,您可以将转换器添加到JsonSerializer中(由Bjørn Egil建议):

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

并且在序列化期间,它将适用于它遇到的每个枚举(由Travis建议)。

  • 或者使用JsonConverter(由banana建议):

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

此外,您可以使用StringEnumConverter(NamingStrategy, Boolean)构造函数来控制大小写和数字是否仍然被接受。


9
点击链接查看如何在ASP.NET MVC应用程序中使用它的说明:http://james.newtonking.com/archive/2008/10/16/asp-net-mvc-and-json-net.aspx。 - RredCat
2
这是该函数的链接:http://james.newtonking.com/projects/json/help/html/T_Newtonsoft_Json_Converters_StringEnumConverter.htm - CAD bloke
67
HttpConfiguration config = GlobalConfiguration.Configuration; config.Formatters.JsonFormatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;config.Formatters.JsonFormatter.SerializerSettings.Converters.Add( new Newtonsoft.Json.Converters.StringEnumConverter()); - Iggy
1
默认情况下,ASP.NET MVC不使用Json.Net作为JSON序列化器,需要扩展Controller或手动覆盖每个序列化。 - Odys
3
您可以对转换器进行自定义(例如,将输出设置为 camelCase ):new StringEnumConverter { CamelCaseText = true } - Seafish
显示剩余5条评论

460

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


如果您可以使用JSON.Net而不是JavaScriptSerializer,那么请参考Omer Bokhari提供的这个问题的答案:JSON.net覆盖了此用例(通过属性[JsonConverter(typeof(StringEnumConverter))])和许多内置的.net序列化器未处理的其他用例。这里是一个比较序列化器功能和功能的链接

8
@Fabzter -- 你的解决方案对我很有用,使用了Newtonsoft的Json。 - BeemerGuy
1
@Json编码大师 是ASP.NET默认使用的序列化器。 - BrainSlugs83
13
这句话的意思是:问题是关于使用JavaScriptSerializer,而不是Json.NET(如果您查看版本历史记录,您会发现有一个编辑来澄清这一点),如果您使用JavaScriptSerializer,则属性JsonConverter将无法使用。 - BornToCode
1
“Json.NET 是 ASP.NET 默认使用的序列化器”-- 当问题被提出或回答时,这并不是真实情况。(但最重要的是回答的清晰度) - ryanwebjackson
[JsonConverter(typeof(StringEnumConverter))] 是用于 Newtonsoft 的。 - Noman_1

191

@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的更多信息,请参考Json.NET页面

此外,若要使枚举成员序列化/反序列化为特定文本,请使用

System.Runtime.Serialization.EnumMember

属性,就像这样:

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

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

14
谢谢!我只是在寻找 [EnumMember] - Poulad
1
CamelCaseText 属性现已被标记为过时。实例化转换器的新方法是:new StringEnumConverter(new CamelCaseNamingStrategy()) - fiat
你是在NET CORE 2中将JsonConvert放置在ConfigureServices或Configure中? - developer learn999

182

将以下代码添加到你的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());

4
出于某些原因,我无法使这个工作。即使采取了这种方法,Fiddler仍然显示顽固的数字2而不是“Warning”。另外,将“Formatting”更改为“Indented”的原因是什么? - sq33G
5
在这个例子中,第三行代码被添加到App_start/webapiconfig.cs文件中,并在ASP.NET Web API 2.1项目中帮我实现了一个小技巧:在REST(json格式)调用中返回枚举值的字符串。 - Greg Z.
1
有没有一种方法可以仅按请求范围设置此属性? - Anestis Kivranoglou
@AnestisKivranoglou 可以使用自定义的 JSON 序列化器,每个请求都有自己的设置。 - BrainSlugs83
3
"indented" 的第一个序列化设置与操作问题无关。 - user3791372
您需要引用 System.Net.Http.FormattingSystem.Web.HttpSystem.Web.Http.WebHost - ᴍᴀᴛᴛ ʙᴀᴋᴇʀ

67
在 .net core 3 中,使用 System.Text.Json 内置类现在可以实现此功能(编辑: 根据 docs,System.Text.Json 也可作为 NuGet 包在 .net core 2.0 和 .net framework 4.7.2 及更高版本中使用):
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 }

3
干净利落。 - jolySoft

46

我不能像@ob.的最佳回答那样改变源模型,也不想像@Iggy一样全局注册它。所以我将https://dev59.com/tnE95IYBdhLWcg3wI6bt#2870420和@Iggy的https://dev59.com/tnE95IYBdhLWcg3wI6bt#18152942组合起来,允许在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()
        }
    })

1
如果您拥有像这样的属性List<someEnumType>,那么这也很好用。 - Bogdan
正如@Bogdan所提到的,这对我来说是解决方案,使List<AnEnumType>属性序列化为每个枚举值的字符串值,而不是数字值。 - John Washam

42

奥默·博卡里和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; }
}

2
我在枚举成员中使用了 JsonPropertyAttribute,并且它可以用于简单的反序列化任务。不幸的是,在使用 JToken 进行手动调整时,它会被忽略。幸运的是,EnumMemberAttribute 的效果非常好。谢谢! - Prolog

33

通过向Gender属性添加ScriptIgnore属性,使其不被序列化,并添加一个GenderString属性,该属性将被序列化,可以轻松实现此操作:

class Person
{
    int Age { get; set; }

    [ScriptIgnore]
    Gender Gender { get; set; }

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

32
让我来解释一下。根据设计模式,这个解决方案不正确。您修改了模型以适应视图的需求,但模型只需包含数据,不关心演示。您需要将此功能移动到另一层。 - RredCat
19
@RredCat,“视图模型”中具有特定于视图的属性并没有错。在我看来,错误在于不将视图模型与域模型分离:http://blogs.msdn.com/b/simonince/archive/2010/01/26/view-models-in-asp-net-mvc.aspx - Mariano Desanze
6
即使按某种规律它是不正确的,但是OP并没有提及这一点,所以这确实是一个正确的答案。(尽管我在哲学上可能同意你的观点。) - MEMark
5
这是使用内置的JavaScriptSerializer实现所需序列化的一种廉价方法,即它回答了这个问题。只有字符串将被序列化,所以我真的看不出有什么问题。如果你想要优雅的解决方案,请使用Json.Net;如果你需要一个快速的解决方法来解决问题,并希望使用内置的序列化程序来使用asmx或页面方法,那么这就是答案。 - Stephen Kennedy
11
这个评论区中那些过于拘泥小节的无聊争论实在是令人着迷。 - Mike Mooney
显示剩余8条评论

33

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


29

这个版本的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; 
        }
    }
}

3
我认为这适用于DataContractJsonSerializer而不是JavaScriptSerializer - KCD
1
简单易用,使用本地.NET框架序列化程序解决了我的问题。 - The Senator
1
最好的解决方案是不允许我使用第三方库(ISO合规问题)。 - Daniel Gruszczyk
当然,这不是问题中所提到的序列化器类型。JavaScriptSerializer会序列化所有未被忽略的内容,而DataContractJsonSerializer需要使用DataMember属性。感谢你的推荐,但请注意我的名字拼错了 :) - Stephen Kennedy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接