我有一个包含 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)是受欢迎的。
我发现 Json.NET 通过 StringEnumConverter
属性提供了我正在寻找的确切功能:
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
[JsonConverter(typeof(StringEnumConverter))]
public Gender Gender { get; set; }
StringEnumConverter
documentation 上提供了更多详细信息。
还有其他地方可以更全局地配置此转换器:
如果您希望枚举始终被序列化/反序列化为字符串,则枚举本身: [JsonConverter(typeof(StringEnumConverter))] enum Gender { Male, Female }
如果有人想避免属性修饰,您可以将转换器添加到您的 JsonSerializer(由 Bjørn Egil 建议):serializer.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());并且它将适用于它在序列化期间看到的每个枚举(由 Travis 建议)。
或 JsonConverter(香蕉建议): JsonConvert.SerializeObject(MyObject, new Newtonsoft.Json.Converters.StringEnumConverter());
此外,您可以使用 StringEnumConverter(NamingStrategy, Boolean) 构造函数控制大小写以及是否仍接受数字。
不,没有可以使用的特殊属性。 JavaScriptSerializer
将 enums
序列化为它们的数值而不是它们的字符串表示形式。您需要使用自定义序列化将 enum
序列化为其名称而不是数值。
如果您可以使用 JSON.Net 而不是 JavaScriptSerializer
,请参阅 OmerBakhari 提供的 answer on this question:JSON.net 涵盖了此用例(通过属性 [JsonConverter(typeof(StringEnumConverter))]
)和许多其他未由内置 .net 序列化程序处理的用例. Here is a link comparing features and functionalities of the serializers。
JsonConverter
不会工作。
将以下内容添加到 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
?
@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())
我无法像(@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>
属性的解决方法。
在 .net core 3 中,这现在可以通过 System.Text.Json 中的内置类实现(edit: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 }
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 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
这很容易通过将 ScriptIgnore
属性添加到 Gender
属性,使其不被序列化,并添加一个 确实 被序列化的 GenderString
属性:
class Person
{
int Age { get; set; }
[ScriptIgnore]
Gender Gender { get; set; }
string GenderString { get { return Gender.ToString(); } }
}
此版本的 Stephen 的 answer 不会更改 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
有效
这是 newtonsoft.json 的答案
enum Gender { Male, Female }
class Person
{
int Age { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
Gender Gender { get; set; }
}
true
添加到您的 JsonConverter 类型,如下所示:[JsonConverter(typeof(StringEnumConverter), true)]
带有 System.Text.Json 的 Asp.Net Core 3
public void ConfigureServices(IServiceCollection services)
{
services
.AddControllers()
.AddJsonOptions(options =>
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())
);
//...
}
如果您不想使用 JsonConverter
属性,也可以将转换器添加到您的 JsonSerializer
:
string SerializedResponse = JsonConvert.SerializeObject(
objToSerialize,
new Newtonsoft.Json.Converters.StringEnumConverter()
);
它适用于在序列化过程中看到的每个 enum
。
这是一个将服务器端 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
}
使用按位 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 的 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>
对于 ASP.Net 核心,只需将以下内容添加到您的启动类:
JsonConvert.DefaultSettings = (() =>
{
var settings = new JsonSerializerSettings();
settings.Converters.Add(new StringEnumConverter { AllowIntegerValues = false });
return settings;
});
您可以通过调用 JsonConverter.SerializeObject 来创建 JsonSerializerSettings,如下所示:
var result = JsonConvert.SerializeObject
(
dataObject,
new JsonSerializerSettings
{
Converters = new [] {new StringEnumConverter()}
}
);
注意到当有描述属性时,序列化没有答案。
这是我支持 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; }
对于 .Net 核心:-
public void ConfigureServices(IServiceCollection services)
{
...
services.AddJsonFormatters(f => f.Converters.Add(new StringEnumConverter()));
...
}
Microsoft.AspNetCore.Mvc.Formatters.Json
NuGet 包中的一个,它似乎只是 IMvcCoreBuilder
上的扩展方法,而不是 IMvcBuilder
。所以它像 services.AddMvcCore().AddJsonFormatters(f => f.Converters.Add(new StringEnumConverter()));
一样使用。
用这个:
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
[Serializable]
[JsonConverter(typeof(StringEnumConverter))]
public enum Gender { Male, Female }
以防万一有人发现上述不足,我最终解决了这个重载:
JsonConvert.SerializeObject(objToSerialize, Formatting.Indented, new Newtonsoft.Json.Converters.StringEnumConverter())
对于 .NET 6.0,如果您想使用内置 JsonSerializer
(System.Text.Json)
然后,它是开箱即用的,您只需要使用内置的 JsonStringEnumConverter
属性。例如:
[JsonConverter(typeof(JsonStringEnumConverter))]
public SomeEnumType EnumProperty { get; set; }
就是这样,但请确保您的 SomeEnumType
包含具有确切字符串值的值,否则它将引发异常。外壳似乎不敏感。
services.AddControllers().AddJsonOptions(a => a.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()))
。你怎么看?
这是一个老问题,但我想我会做出贡献以防万一。在我的项目中,我对任何 Json 请求都使用单独的模型。模型通常与带有“Json”前缀的域对象具有相同的名称。使用 AutoMapper 映射模型。通过让 json 模型声明一个作为域类枚举的字符串属性,AutoMapper 将解析为它的字符串表示形式。
如果您想知道,我需要 Json 序列化类的单独模型,因为否则内置序列化程序会提供循环引用。
希望这可以帮助某人。
您实际上可以使用 JavaScriptConverter 通过内置的 JavaScriptSerializer 来完成此操作。通过将枚举转换为 Uri,您可以将其编码为字符串。
我已经描述了如何为日期执行此操作,但它也可以用于枚举。 Custom DateTime JSON Format for .NET JavaScriptSerializer。
一个更面向未来的选择
面对同样的问题,我们确定我们需要一个自定义版本的 StringEnumConverter
来确保我们的枚举值可以随着时间的推移而扩展,而不会在反序列化方面发生灾难性的中断(请参阅下面的背景)。即使负载包含没有命名定义的枚举值,使用下面的 SafeEnumConverter
也可以完成反序列化,更接近于 int-to-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
会引发类似于“将值 SomeString 转换为类型 EnumType 时出错”的 JsonSerializationException
,然后整个反序列化过程失败。这对我们来说是一个交易破坏者,因为即使客户端计划忽略/丢弃它不理解的属性值,它仍然需要能够反序列化其余的有效负载!
StringEnumConveter
:仍然会引发异常,但它会导致整个反序列化过程失败(并且它很可能在堆栈中更远的地方被捕获)。您是否需要这种容错是您的用例的产物。
不确定这是否仍然相关,但我不得不直接写入一个 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“规则”开始的小写字母。将其格式化为干净的缩进并忽略输出中的空值。 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)
我已使用 Newtonsoft.Json
库将该解决方案的所有部分组合在一起。它修复了枚举问题,也使错误处理变得更好,并且可以在 IIS 托管服务中运行。代码非常多,因此您可以在 GitHub 上找到它:https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs
您必须在 Web.config
中添加一些条目才能使其工作,您可以在此处查看示例文件:https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config
对于在 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();
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();
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]
);
Controller
或手动覆盖每个序列化。camelCase
输出):new StringEnumConverter { CamelCaseText = true }