如何使 JSON.NET StringEnumConverter 使用连字符分隔的大小写

本文关键字:连字符 分隔 大小写 何使 JSON NET StringEnumConverter | 更新日期: 2023-09-27 17:57:01

我使用一个 API,它返回如下字符串值:

some-enum-value

我尝试将这些值放在一个 enum 中,因为默认StringEnumConverter没有做我想要的,那就是用一些额外的逻辑装饰这个转换器。

如何确定值已正确反序列化

以下代码是我完成这项工作的试用。
然而这条线

reader = new JsonTextReader(new StringReader(cleaned));

破坏整个事情,因为base.ReadJson无法将字符串识别为 JSON。

有没有更好的方法来做到这一点,而不必在StringEnumConverter中实现所有现有逻辑?
我该如何修复我的方法?

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));
    }
}

如何使 JSON.NET StringEnumConverter 使用连字符分隔的大小写

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

例:

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中轻松指定转换器和其他设置。

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

new JsonSerializerSettings
{
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
    Converters = new List<JsonConverter> { new StringEnumConverter { CamelCaseText = true } },
    NullValueHandling = NullValueHandling.Ignore
};

您还可以使用此代码:

[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,这变得更加容易:

新功能 - 添加了对 StringEnumConverter 的命名策略的支持

Json.NET 12.0.3 为连字符分隔的烤肉串外壳添加了KebabCaseNamingStrategy,如 some-enum-value

新功能 - 添加了烤肉串案例命名策略

使用

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

具体来说,在 12.0.3 及更高版本中,您可以将KebabCaseNamingStrategy传递到多个构造函数中的任何一个中,以便在构造转换器并将其添加到JsonSerializerSettings.ConvertersStringEnumConverter

var settings = new JsonSerializerSettings
{
    Converters = { new StringEnumConverter(new KebabCaseNamingStrategy()) },
};
var json = JsonConvert.SerializeObject(MyEnum.SomeEnumValue, settings);
Assert.IsTrue(json == "'"some-enum-value'""); // Passes successfully

这样做后,序列化的枚举值现在将采用烤肉串大小写。 演示小提琴#1在这里。

在 Json.NET 12.0.1 和 12.0.2 中,您可以通过子类化来定义自己的烤肉串大小写命名策略SnakeCaseNamingStrategy

public class KebabCaseNamingStrategy : SnakeCaseNamingStrategy
{
    protected override string ResolvePropertyName(string name)
    {
        return base.ResolvePropertyName(name).Replace('_', '-');
    }
}

演示小提琴#2在这里。

您也可以使用此方法:

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();
        }

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;
        }

枚举应该具有描述属性。喜欢这个:

public enum MyEnum
    {
        [Description("some-enum-value")]
        And,
        [Description("some-enum-value")]
        Or
    }

你可以像这样使用你的enum

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