通过修饰覆盖一个特定枚举的全局Json.NET枚举处理



我有一堆枚举,希望Json.NET将其序列化为基于字符串的字符串。我在Global.asax.cs文件中有以下内容,它运行得很好:

HttpConfiguration config = GlobalConfiguration.Configuration;
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter { CamelCaseText = true });

这使得它成为这样一个枚举:

public enum FavoriteWebSite {
StackOverflow,
GoogleNews
// Etc
}

将序列化为"stackOverflow"、"googleNews"等值。

然而,我有几个枚举是逐位掩码。为了使这成为一个简单的例子,假设一个看起来像这样:

public enum Hobbies {
Walking = 0x01,
Biking = 0x02,
// Etc
}

当我序列化这个枚举的实例时,会发生什么取决于其中的值类型。例如:

Hobbies set1 = Hobbies.Walking;                  // Serializes as "walking" -- bad
Hobbies set2 = Hobbies.Walking | Hobbies.Biking; // Serializes as "3"       -- good!

我想重写此枚举上的序列化,使其仅序列化为int,同时保留全局设置以使用基于骆驼的字符串。

我尝试删除全局配置,以便默认情况下枚举被序列化为int,然后只向非位掩码枚举添加[JsonConverter(typeof(StringEnumConverter))]。然而,这会导致PascalCased,而不是CamelCased的序列化。在上面的方法装饰中使用StringEnumConverter时,我没有看到任何方法可以获得CamelCaseText属性集。

因此,概括一下,目标是:

  1. 将单值枚举序列化为pascalCased字符串
  2. 将位掩码枚举序列化为int

谢谢!

您的主要困难似乎是没有用FlagsAttribute装饰您的标志枚举,如下所示:

[Flags]
public enum Hobbies
{
Walking = 0x01,
Biking = 0x02,
// Etc
}

这是针对标志枚举的推荐最佳实践:

设计标志枚举

√将System.FlagsAttribute应用于标志枚举。不要将此属性应用于简单枚举。

另请参阅此处。如果不这样做,许多与枚举相关的.Net实用程序可能无法按预期用于标记枚举。

完成此操作后,StringEnumConverter将把具有复合值的标志枚举序列化为一组逗号分隔的值,而不是您当前看到的数值:

{
"Hobbies": "walking, biking"
}

如果您不希望这样,并且仍然希望在JSON中看到标志枚举的默认数值,则可以将StringEnumConverter子类化为仅转换非标志枚举:

public class NonFlagStringEnumConverter : StringEnumConverter
{
public override bool CanConvert(Type objectType)
{
if (!base.CanConvert(objectType))
return false;
return !HasFlagsAttribute(objectType);
}
static bool HasFlagsAttribute(Type objectType) 
{ 
return Attribute.IsDefined(Nullable.GetUnderlyingType(objectType) ?? objectType, typeof(System.FlagsAttribute));
}
}

然后像这样使用:

config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new NonFlagStringEnumConverter  { CamelCaseText = true });

这将导致Json.NET回退到枚举的任何全局默认Json转换器,或者如果没有适用的回退,则回退到数值序列化。在这里演示小提琴#1。

此外,如果您需要取代在更高级别应用的转换器,并强制对标志枚举进行数字序列化,请使用以下命令:

public class ForceNumericFlagEnumConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
if (!(Nullable.GetUnderlyingType(objectType) ?? objectType).IsEnum)
return false;
return HasFlagsAttribute(objectType);
}
public override bool CanRead { get { return false; } }
public override bool CanWrite { get { return false; } }
static bool HasFlagsAttribute(Type objectType) 
{ 
return Attribute.IsDefined(Nullable.GetUnderlyingType(objectType) ?? objectType, typeof(System.FlagsAttribute));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}

在这里演示小提琴#2。

这篇博客文章很好地解释了没有内置的方法来覆盖全局StringEnumConverter。您需要编写自己的转换器,使不执行任何操作,然后在转换JSON.NET时,它将返回到该类型的默认转换器(对于枚举,它将序列化为其数值)。

所以,如果你有全局转换器:

config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new StringEnumConverter { CamelCaseText = true });

您可以定义此ForceDefaultConverter转换器

public class ForceDefaultConverter : JsonConverter
{
public override bool CanRead => false;
public override bool CanWrite => false;
public override bool CanConvert(Type objectType) => throw new NotImplementedException();
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => throw new NotImplementedException();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}

并在要覆盖默认StringEnumConverter的属性上使用它。

public class ExampleDto
{
[JsonConverter(typeof(ForceDefaultConverter))]
public TestEnum EnumValue { get; set; }
}

如果在序列化具有此枚举类型的所有对象时需要此数值,则在枚举类型本身上执行此操作。

[JsonConverter(typeof(ForceDefaultConverter))]
public enum TestEnum
{
Foo = 1,
Bar = 2
}

相关内容

  • 没有找到相关文章

最新更新