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

我有一个包含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)是受欢迎的。

658115 次浏览

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


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

我发现Json.NET提供了我正在寻找的StringEnumConverter属性的确切功能:

using Newtonsoft.Json;using Newtonsoft.Json.Converters;
[JsonConverter(typeof(StringEnumConverter))]public Gender Gender { get; set; }

更多细节可在#0留档

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

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

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

    并且它将在序列化期间看到的每个枚举都有效(由特拉维斯建议)。

  • 或JsonConver(由香蕉建议):

    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.

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

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

这很容易通过向Gender属性添加#0属性来完成,导致它不被序列化,并添加GenderString属性,确实被序列化:

class Person{int Age { get; set; }
[ScriptIgnore]Gender Gender { get; set; }
string GenderString { get { return Gender.ToString(); } }}

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

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

希望这有助于某人。

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

这是一个简单的解决方案,它将服务器端C#枚举序列化为JSON并使用结果填充客户端<select>元素。这适用于简单枚举和位标志枚举。

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

下面是:

示例枚举

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

使用按位ORs生成权限系统的复杂枚举。因此您不能依赖简单的索引[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() helper方法-但不要担心,您可以使用任何标准的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

超文本标记语言

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

超文本标记语言

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

将以下内容添加到c#enum作为字符串的JSON序列化global.asax

  HttpConfiguration config = GlobalConfiguration.Configuration;config.Formatters.JsonFormatter.SerializerSettings.Formatting =Newtonsoft.Json.Formatting.Indented;
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());

我无法像(@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()}})

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

但是为了使它也能与临时序列化一起工作,请将以下内容添加到您的开始类中(如Global.asaxApplication_Start)

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

更多信息在Json.NET页

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

系统。运行时。序列化。E num成员

属性,像这样:

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

以下是newtonsoft.json答案

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

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

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

注意到,当有描述属性时,序列化没有答案。

这是我的支持描述属性的实现。

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

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

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

它将适用于它在序列化期间看到的每个enum

new JavaScriptSerializer().Serialize((from pin (new List<Person>() {new Person(){Age = 35,Gender = Gender.Male}})select new { Age =p.Age, Gender=p.Gender.ToString() }).ToArray()[0]);

以防有人发现上述不足,我最终解决了这个超载:

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

我已经使用Newtonsoft.Json库将这个解决方案的所有部分放在一起。它修复了枚举问题,也使错误处理变得更好,并且它可以在IIS托管服务中工作。这是相当多的代码,所以你可以在GitHub上找到它在这里:https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

您必须在Web.config中添加一些条目才能使其工作,您可以在此处看到一个示例文件:https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config

对于. Net Core:-

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

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

ASP.NET核心方式:

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核心,只需将以下内容添加到您的启动类中:

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

不确定这是否仍然相关,但我不得不直接写入一个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“规则”以小写开头的。将其格式化为清晰的缩进并忽略输出中的空值。通过添加StringEnumConver,它会打印带有字符串值的枚举。

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

用法:

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

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

Dim sec = New Newtonsoft.Json.Converters.StringEnumConverter()sec.NamingStrategy() = New Serialization.CamelCaseNamingStrategy
Dim JSON_s As New JsonSerializerJSON_s.Converters.Add(sec)
Dim jsonObject As JObjectjsonObject = JObject.FromObject(SomeObject, JSON_s)Dim text = jsonObject.ToString
IO.File.WriteAllText(filePath, text)

一个稍微更面向未来的选择

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

用法:

[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抛出一个类似于“将值转换为类型EnumType的错误”的JsonSerializationException,然后整个反序列化过程失败。这对我们来说是一个交易破坏者,因为即使客户端计划忽略/丢弃它不理解的属性值,它仍然需要能够反序列化其余的有效负载!

在. net core 3中,现在可以使用System. Text. Json(编辑:System. Text. Json也可作为. net core 2.0和. net Framework 4.7.2及更高版本的NuGet包提供,根据文档)中的内置类:

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 }

Asp. Net Core 3与System. Text. Json

public void ConfigureServices(IServiceCollection services){
services.AddControllers().AddJsonOptions(options =>options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
//...}
        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();

使用这个:

using Newtonsoft.Json;using Newtonsoft.Json.Converters;
[Serializable][JsonConverter(typeof(StringEnumConverter))]public enum Gender { Male, Female }

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

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

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

仅此而已,但请确保您的SomeEnumType包含具有确切字符串值的值,否则它将抛出异常。大小写似乎不敏感。

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

对于任何需要在'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();