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

时间:2015-04-10 23:09:28

标签: json asp.net-mvc serialization json.net

我有一堆枚举,我希望Json.NET序列化为camelcased字符串。我在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,同时保留全局设置以使用camelcased字符串完整。

我尝试删除全局配置,以便默认情况下将枚举序列化为整数,然后仅将[JsonConverter(typeof(StringEnumConverter))]添加到非位掩码枚举中。但是,这导致了PascalCased,而不是CamelCased序列化。在上面的方法修饰中使用StringEnumConverter时,我没有看到任何方法来设置CamelCaseText属性。

所以,回顾一下,目标是:

  1. 将单值枚举序列化为pascalCased字符串。
  2. 将位掩码枚举序列化为整数。
  3. 谢谢!

2 个答案:

答案 0 :(得分:1)

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

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

这是标记枚举的recommended best practice

  

设计标记枚举

     

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

另见here。如果您不这样做,许多与枚举相关的.Net实用程序可能无法按预期执行标记枚举。

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

{
  "Hobbies": "walking, biking"
}

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

public class NonFlagStringEnumConverter : StringEnumConverter
{
    public override bool CanConvert(Type objectType)
    {
        if (!base.CanConvert(objectType))
            return false;
        Type underlyingType = (Nullable.GetUnderlyingType(objectType) ?? objectType);

        // Check if the enum has [Flags] applied, return false if true.
        var attributes = underlyingType.GetCustomAttributes(typeof(FlagsAttribute), false);
        return attributes.Length == 0;
    }
}

然后使用它:

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

答案 1 :(得分:1)

This 博客文章很好地解释了没有内置的方法来覆盖全局 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
}