提问人:Omer Bokhari 提问时间:3/14/2010 最后编辑:John SmithOmer Bokhari 更新时间:5/10/2023 访问量:723896
JavaScriptSerializer - 将枚举序列化为字符串
JavaScriptSerializer - JSON serialization of enum as string
问:
我有一个包含属性的类,并且在使用 序列化对象时,我的 json 结果包含枚举的整数值而不是它的“名称”。有没有办法在我的 json 中获取枚举作为 a,而无需创建自定义?也许我可以用一个属性来装饰定义或对象属性?enum
JavaScriptSerializer
string
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)。
答:
不,没有可以使用的特殊属性。 序列化为它们的数值,而不是它们的字符串表示形式。您需要使用自定义序列化将 序列化为其名称而不是数值。JavaScriptSerializer
enums
enum
如果您可以使用 JSON.Net 而不是 而不是查看 Omer Bokhari 提供的此问题的答案: JSON.net 涵盖了此用例(通过属性)以及内置 .net 序列化程序未处理的许多其他用例。下面是一个链接,比较了序列化程序的特性和功能。JavaScriptSerializer
[JsonConverter(typeof(StringEnumConverter))]
评论
JsonConverter
我发现 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) 构造函数控制大小写以及是否仍接受数字。
评论
Controller
camelCase
new StringEnumConverter { CamelCaseText = true }
实际上,您可以使用 JavaScriptConverter 通过内置的 JavaScriptSerializer 来实现此目的。通过将枚举转换为 Uri,可以将其编码为字符串。
我已经描述了如何为日期执行此操作,但它也可用于枚举。.NET JavaScriptSerializer 的自定义 DateTime JSON 格式。
这可以通过向属性添加 ScriptIgnore
特性(使其不序列化)并添加一个序列化的属性来轻松完成:Gender
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;
}
}
}
评论
DataContractJsonSerializer
JavaScriptSerializer
下面是一个简单的解决方案,它将服务器端 C# 枚举序列化为 JSON,并使用结果填充客户端元素。这适用于简单枚举和 bitflag 枚举。<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
}
使用按位 OR 生成权限系统的复杂枚举。因此,您不能依赖简单索引 [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 的帮助程序方法 - 但不用担心,您可以使用任何标准的 JSON 格式化程序,因为枚举已经投影到一个简单的匿名类型中,可以进行序列化。Response.AsJson()
生成的 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>
将以下内容添加到 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());
评论
Formatting
Indented
我无法像(@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()
}
})
评论
List<AnEnumType>
@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;
});
此外,要让您的枚举成员序列化/反序列化为特定文本/从特定文本序列化/反序列化,请使用
System.Runtime.Serialization.EnumMember
属性,如下所示:
public enum time_zone_enum
{
[EnumMember(Value = "Europe/London")]
EuropeLondon,
[EnumMember(Value = "US/Alaska")]
USAlaska
}
评论
[EnumMember]
CamelCaseText
new StringEnumConverter(new CamelCaseNamingStrategy())
这是newtonsoft.json的答案
enum Gender { Male, Female }
class Person
{
int Age { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
Gender Gender { get; set; }
}
评论
true
[JsonConverter(typeof(StringEnumConverter), true)]
可以通过调用 JsonConverter.SerializeObject 创建 JsonSerializerSettings,如下所示:
var result = JsonConvert.SerializeObject
(
dataObject,
new JsonSerializerSettings
{
Converters = new [] {new StringEnumConverter()}
}
);
请注意,当存在 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; }
如果您不想使用属性,也可以向 T 添加转换器:JsonSerializer
JsonConverter
string SerializedResponse = JsonConvert.SerializeObject(
objToSerialize,
new Newtonsoft.Json.Converters.StringEnumConverter()
);
它将适用于该序列化期间看到的每个人。enum
new JavaScriptSerializer().Serialize(
(from p
in (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())
评论
我已使用该库将此解决方案的所有部分放在一起。它修复了枚举问题,还使错误处理变得更好,并且它适用于 IIS 托管服务。它有相当多的代码,所以你可以在 GitHub 上找到它:https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.csNewtonsoft.Json
您必须添加一些条目才能使其工作,您可以在此处查看示例文件: https://github.com/jongrant/wcfjsonserializer/blob/master/Web.configWeb.config
对于 .Net Core :-
public void ConfigureServices(IServiceCollection services)
{
...
services.AddJsonFormatters(f => f.Converters.Add(new StringEnumConverter()));
...
}
评论
Microsoft.AspNetCore.Mvc.Formatters.Json
IMvcCoreBuilder
IMvcBuilder
services.AddMvcCore().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; }
}
评论
JsonPropertyAttribute
JToken
EnumMemberAttribute
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“规则”开始。将其格式化为干净的缩进,并忽略输出中的 null。Aslo 通过添加 StringEnumConverter,它打印带有字符串值的枚举。
就我个人而言,我认为这是我能想到的最干净的,而不必用注释弄脏模型。
用法:
internal void SaveJson(string fileName)
{
// serialize JSON directly to a file
using (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 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)
一个更面向未来的选择
面对同样的问题,我们确定我们需要一个自定义版本,以确保我们的枚举值可以随着时间的推移而扩展,而不会在反序列化方面发生灾难性中断(见下面的背景)。使用以下命令可以完成反序列化,即使有效负载包含没有命名定义的枚举的值,也更接近于整数到枚举转换的工作方式。StringEnumConverter
SafeEnumConverter
用法:
[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;
}
}
背景
当我们考虑使用 时,我们遇到的问题是,当添加新枚举值时,我们还需要被动性,但并非每个客户端都能立即意识到新值。在这些情况下,使用 Newtonsoft JSON 打包会抛出类似于“将值 SomeString 转换为类型 EnumType 时出错”,然后整个反序列化过程失败。这对我们来说是一个交易破坏者,因为即使客户端计划忽略/丢弃它不理解的属性值,它仍然需要能够反序列化其余的有效负载!StringEnumConverter
StringEnumConverter
JsonSerializationException
评论
StringEnumConveter
在 .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 }
评论
使用 System.Text.Json Asp.Net Core 3
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,如果要使用内置 (System.Text.Json)JsonSerializer
然后,它是开箱即用的,你只需要使用内置属性。例如:JsonStringEnumConverter
[JsonConverter(typeof(JsonStringEnumConverter))]
public SomeEnumType EnumProperty { get; set; }
就是这样,但请确保你的包含值与确切的字符串值,否则它将引发异常。外壳似乎不敏感。SomeEnumType
评论
services.AddControllers().AddJsonOptions(a => a.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()))
对于在 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();
命名空间 System.Text.Json.Serialization 具有 JsonStringEnumConverter,可以按如下方式使用。 [JsonConverter(typeof(JsonStringEnumConverter))]
评论