关于c#:如何装饰JSON.NET StringEnumConverter

How to decorate JSON.NET StringEnumConverter

我正在使用一个API,它返回这样的字符串值。 some-enum-value

我尝试将这些值放在一个枚举中,因为默认的StringEnumConverter不能完成工作,因此我尝试使用一些其他逻辑来装饰此Converter。 如何确保正确反序列化了这些值?

以下代码是我完成此工作的尝试。 但是,由于base.ReadJson无法将字符串识别为JSON,因此reader = new JsonTextReader(new StringReader(cleaned));行破坏了整个过程。

是否有更好的方法可以执行此操作而不必在StringEnumConverter中实现所有现有逻辑? 如何解决我的方法?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class BkStringEnumConverter : StringEnumConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.String)
        {
            var enumString = reader.Value.ToString();
            if (enumString.Contains("-"))
            {
                var cleaned = enumString.Split('-').Select(FirstToUpper).Aggregate((a, b) => a + b);
                reader = new JsonTextReader(new StringReader(cleaned));
            }
        }
        return base.ReadJson(reader, objectType, existingValue, serializer);
    }

    private static string FirstToUpper(string input)
    {
        var firstLetter = input.ToCharArray().First().ToString().ToUpper();
        return string.IsNullOrEmpty(input)
            ? input
            : firstLetter + string.Join("", input.ToCharArray().Skip(1));
    }
}

我通过在枚举值上添加EnumMember属性解决了该问题。 Json.NET的默认StringEnumConverter完美地处理了这些属性。

例:

1
2
3
4
5
6
7
8
public enum MyEnum
{
    [EnumMember(Value ="some-enum-value")]
    SomeEnumValue,
    Value,
    [EnumMember(Value ="some-other-value")]
    SomeOtherValue
}

请注意,您只需要指定属性即可,例如破折号或其他不能在枚举中使用的特殊字符。大写小写由StringEnumConverter处理。因此,如果服务返回类似someenumvalue的值,则应在枚举someenumvalue中像这样使用它。如果您喜欢someenumvalue,则应使用EnumMember属性。万一服务像这样someenumvalue返回它,您就可以像这样someenumvalue使用它(当您使用CamelCaseText属性时,它是开箱即用的)。

您可以在JsonSerializerSettings中轻松指定转换器和其他设置。

这是我自己使用的设置的示例。

1
2
3
4
5
6
new JsonSerializerSettings
{
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
    Converters = new List<JsonConverter> { new StringEnumConverter { CamelCaseText = true } },
    NullValueHandling = NullValueHandling.Ignore
};


您还可以使用以下代码:

1
2
3
4
5
6
7
8
9
10
[JsonConverter(typeof(StringEnumConverter))]
public enum ResposeStatus
{
    [EnumMember(Value ="success value")]
    Success,
    [EnumMember(Value ="fail value")]
    Fail,
    [EnumMember(Value ="error value")]
    Error
};

序列化JsonConvert.Serialize()时,将使用EnumMember中的文本。


在Json.NET 12.0.1中,通过向StringEnumConverter添加NamingStrategy使其变得更容易:

New feature - Added support for NamingStrategy to StringEnumConverter

首先,由于Json.NET没有内置的破折号命名策略,因此可以通过将SnakeCaseNamingStrategy子类化来定义以下内容:

1
2
3
4
5
6
7
public class DashCaseNamingStrategy : SnakeCaseNamingStrategy
{
    protected override string ResolvePropertyName(string name)
    {
        return base.ResolvePropertyName(name).Replace('_', '-');
    }
}

现在,在构造转换器并将其添加到JsonSerializerSettings.Converters时,可以将其传递给StringEnumConverter的多个构造函数中的任何一个:

1
2
3
4
5
6
7
var settings = new JsonSerializerSettings
{
    Converters = { new StringEnumConverter(typeof(DashCaseNamingStrategy)) },
};
var json = JsonConvert.SerializeObject(MyEnum.SomeEnumValue, settings);

Assert.IsTrue(json ==""some-enum-value""); // Passes successfully

使用此方法,MyEnum根本不需要注释。


您也可以使用以下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static string GetDescription(this Enum member)
        {
            if (member.GetType().IsEnum == false)
                throw new ArgumentOutOfRangeException(nameof(member),"member is not enum");

            var fieldInfo = member.GetType().GetField(member.ToString());

            if (fieldInfo == null)
                return null;

            var attributes = fieldInfo.GetCustomAttributes<DescriptionAttribute>(false).ToList();

            return attributes.Any() ? attributes.FirstOrDefault()?.Description : member.ToString();
        }

要么

1
2
3
4
5
6
7
8
public static string GetDescription(this object member)
        {
            var type = member.GetType();

            var attributes = type.GetCustomAttributes<DescriptionAttribute>(false).ToList();

            return attributes.Any() ? attributes.FirstOrDefault()?.Description : member.GetType().Name;
        }

枚举应具有destription属性。像这样:

1
2
3
4
5
6
7
8
public enum MyEnum
    {
        [Description("some-enum-value")]
        And,
        [Description("some-enum-value")]
        Or

    }

然后您可以像这样使用enum

1
MyEnum.GetDescription(); //return"some-enum-value"