【问题标题】:JSON.NET - custom enum handling at deserializationJSON.NET - 反序列化时的自定义枚举处理
【发布时间】:2018-02-08 07:14:33
【问题描述】:

我们有一个可以反序列化为自定义域模型的 JSON,完全没有问题。它包括一个自定义枚举的属性:

public enum UserType
{
    President,
    Chump
}

我们现在更改了枚举类,但仍需要接受并反序列化任何到达的 JSON 的先前值。就像我们现在有两个版本的 JSON

public enum UserType
{
    President,
    Vice-President,
    Citizen  // Chump maps to Citizen, now.
}

在 json 本身中..

"userType": "chump"; // needs to map to Citizen

我不知道该怎么做。

这是使用JsonConverter 吗?

此外,这是我们用于所有序列化和反序列化的自定义设置。注意:我们将任何枚举序列化为 string 描述/值,而不是 int 值。

internal static JsonSerializerSettings JsonSerializerSettings => new JsonSerializerSettings
{
    Converters = new JsonConverter[]
    {
        new StringEnumConverter()
    },
    Formatting = Formatting.Indented
};

【问题讨论】:

    标签: c# json enums


    【解决方案1】:

    在您的 Enum 中添加 EnumMember 属性,指定序列化/反序列化过程的值。

    public enum UserType
    {
        President,
        VicePresident,
    
        [EnumMember(Value = "chump")]
        Citizen  // Chump maps to Citizen, now.
    }
    

    当在您的 json 中,userType 属性等于 "Chump""Citizen" 时,属性 userType 将为 Citizen

    记得将System.Runtime.Serialization 引用添加到您的项目中。

    编辑

    我注意到EnumMember 属性的Value 属性的检查区分大小写。所以你不能使用"Chump",如果你的json中有"chump"。要解决此问题,您可以使用自定义StringEnumConverter

    public class UserTypeEnumConverter : StringEnumConverter
    {
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var userTypeType = typeof(UserType);
            if (objectType == userTypeType)
            {
                var value = reader.Value.ToString().ToLower();
    
                foreach (var item in Enum.GetNames(userTypeType))
                {
                    var memberValue = GetEnumMemberValue(userTypeType.GetMember(item)[0]);
                    if (memberValue != null && memberValue.ToLower() == value)
                    {
                        return Enum.Parse(userTypeType, item);
                    }
                }
            }
    
            return base.ReadJson(reader, objectType, existingValue, serializer);
        }
    }
    
    private static string GetEnumMemberValue(MemberInfo memberInfo)
    {
        var attributes = memberInfo.GetCustomAttributes(typeof(EnumMemberAttribute), inherit: false);
    
        if (attributes.Length == 0) return null;
    
        return ((EnumMemberAttribute)attributes[0]).Value;
    }
    

    在上面的代码中,我只检查EnumMember 属性,因为默认StringEnumConvert 已经完成UserType 的成员不区分大小写检查。

    请注意,由于检查,此转换器仅适用于您的 UserType 枚举:

    var userTypeType = typeof(UserType);
    if (objectType == userTypeType)
    {
    

    JsonSerializerSettings初始化替换为:

    internal static JsonSerializerSettings JsonSerializerSettings => new JsonSerializerSettings
    {
        Converters = new JsonConverter[]
        {
            new UserTypeEnumConverter()
        },
        Formatting = Formatting.Indented
    };
    

    我假设Vice-President 枚举器是VicePresident in UserType

    【讨论】:

    • Ta 的努力 - 真的很有帮助。甚至还展示了如何在 JsonSerializerSettings 类中使用它。不过,我将向@martin 提供答案,因为我发现他的ReadJson 更易于阅读且更简洁。但我非常感谢您的努力和帮助。不过,它帮助了我。 (我不能给出 2x 滴答声:()
    • 没问题,很高兴有帮助:)
    • 如果您在反序列化字符串枚举时遇到问题,并且您怀疑它是由值的大小写引起的,请仔细检查您的类型是否实际上是一个枚举开头。我刚刚失去了一个小时的故障排除为什么“GET”不会反序列化为 HttpMethod 认为 HttpMethod 是一个枚举。
    【解决方案2】:

    您可以实现从StringEnumConverter 派生的自定义Converter

    public class UserTypeEnumConverter : StringEnumConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(UserType);
        }
    
        public override object ReadJson(JsonReader reader, 
                                        Type objectType, 
                                        object existingValue, 
                                        JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
            {
                var isNullable = (Nullable.GetUnderlyingType(objectType) != null);
                if (!isNullable)
                {
                    throw new JsonSerializationException();
                }
                return null;
            }
    
            var token = JToken.Load(reader);
            var value = token.ToString();
            if (string.Equals(value, "chump", StringComparison.OrdinalIgnoreCase))
            {
                return UserType.Citizen;
            }
            else
            {
                return base.ReadJson(reader, objectType, existingValue, serializer);
            }               
        }
    }
    

    转换器处理底层类型也可以为空并且仅在反序列化类上声明的属性类型为UserType 时调用的情况。然后它会检查被解析的值是否是“弃用”的值,如果不是,它将读取委托给基址StringEnumConverter

    【讨论】:

    • 甜蜜!完全理解它:) ta!
    • 另外,只是对其进行了编辑以使其更具可读性并将isNullable inside == JsonToken.Null 检查...为什么要检查它是否可以为空所有那些不是JsonToken.NullTokenTypes?浪费未使用的 CPU(是的,是的……微选……等等)。
    • 太棒了:-)!很高兴它有帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-04
    • 1970-01-01
    • 2017-06-30
    • 1970-01-01
    相关资源
    最近更新 更多