无法使用Json.NET将枚举正确转换为JSON

28

我有一个枚举:

public enum Animal 
{ 
    Dog, 
    Cat, 
    BlackBear 
}

我需要将它发送到第三方API。该API要求我发送的枚举值为小写字母,有时需要使用下划线。一般来说,他们要求的名称与我使用的枚举命名约定不匹配。

使用https://gooddevbaddev.wordpress.com/2013/08/26/deserializing-c-enums-using-json-net/提供的示例,我尝试使用自定义JsonConverter:

public class AnimalConverter : JsonConverter {
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
        var animal = (Animal)value;
        switch (animal)
        {
            case Animal.Dog:
            {
                writer.WriteValue("dog");
                break;
            }
            case Animal.Cat:
            {
                writer.WriteValue("cat");
                break;
            }
            case Animal.BlackBear:
            {
                writer.WriteValue("black_bear");
                break;
            }
        }
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
        var enumString = (string)reader.Value;
        Animal? animal = null;
        switch (enumString)
        {
            case "cat":
            {
                animal = Animal.Cat;
                break;
            }
            case "dog":
            {
                animal = Animal.Dog;
                break;
            }
            case "black_bear":
            {
                animal = Animal.BlackBear;
                break;
            }
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(string);
    }
}

回到类的属性中,我这样将属性放在动物上:

[JsonProperty("animal")]
[JsonConverter(typeof(AnimalConverter))]
public Animal ZooAnimals { get; set; }
当我运行程序时,它似乎完全忽略了JsonConverter,而不是看到预期的值,例如"black_bear"或"dog",我看到了"BlackBear"和"Dog"。我该如何让JsonConverter实际执行从枚举值名称到我指定的字符串的转换,并替换该值?谢谢!

你能否编写一个简短但完整的程序,例如LINQPad来演示问题?我编写了一个小的LINQPad程序进行测试,并使用此处的转换器。 - Lasse V. Karlsen
在你的代码示例中,你提到了 AnimalAnimals,能否澄清一下这两个都是指你的枚举?值得一提的是,我使用以下代码片段使你的代码工作正常: Zoo zoo = new Zoo { ZooAnimals = Animal.Cat }; string json = JsonConvert.SerializeObject(zoo); - robyaw
3个回答

81

您不需要编写自己的转换器。Json.NET的StringEnumConverter将读取EnumMember属性。如果您将enum更改为此内容,则会序列化要使用的值。

[JsonConverter(typeof(StringEnumConverter))]
public enum Animals 
{
    [EnumMember(Value = "dog")]
    Dog, 
    [EnumMember(Value = "cat")]
    Cat, 
    [EnumMember(Value = "black_bear")]
    BlackBear 
}

(作为一个小注,由于Animals不是标志枚举,它应该是单数形式:Animal。您应该考虑将其更改为这样。)


嗨,Tim,我如何在.Net 2.0中使用EnumMember? - Ricky Jiao
@RickyJiao 我认为我会创建自己的转换器,基于StringEnumConverter参见源代码),并使其与我自己的EnumMemberAttribute类一起工作,该类基于EnumMemberAttribute(只需要一个属性和构造函数)。 - Tim S.
你知道为什么 StringEnumConverter 使用 EnumMember 而不是 Json.NET 的 JsonProperty 吗?因为我在其他地方都使用 JsonProperty,但显然它不能与枚举一起使用。 - Kapé
1
@Kapé 我不能说我真的这样做了;它似乎很奇怪使用内置类型而不是自定义类型,因为通常在其他地方都是这样做的。这是我的猜测:枚举成员不代表属性(即键/值对中的键),而是值,因此使用 JsonProperty 来描述它是没有意义的。 - Tim S.
2
似乎你需要为枚举类型添加DataContract属性,以使EnumMember正常工作,因为上面的方法对我来说不起作用。像这样:[DataContract] public enum Animals.... - aup
显示剩余3条评论

0
// Might return null, better to use try catch
public static Animals GetEnum(string val)
{
    return (Animals)Enum.Parse(typeof(Animals), val, true);
}

public static string GetName(Animals an)
{
    return Enum.GetName(typeof(Animals), an);
}

public static string GetReplace(Animals an)
{
    var get = GetName(an);
    var tempstr = "";
    int getch = 0;
    foreach (var chr in get.ToCharArray())
    {
        if (chr == chr.ToUpper())
        {
            getch++;
            // Second up value char
            if (getch == 2)
            {
                tempstr += "_" + chr;
            }
            else
            {
                tempstr += chr;
            }
        }
        else
        {
             tempstr += chr;
        }
    }
    return tempstr;
}

无法使用该方法,因为枚举值的名称偶尔会更改。例如,对于一个枚举值“BlackBear”,它必须转换为显示值“black_bear”。 - Whit Waldo
@Xaniff GetEnum(val.Replace("_", "").Replace(" ", "")); 并查看编辑后,使用此函数:GetReplace(animal); - 111WARLOCK111
@Xaniff 如果你不能使用uppervalues,将最终字符串设为`GetReplace(animal).ToLower()'。 - 111WARLOCK111
1
我想我真的在寻找一种更通用的解决方案,这样我就可以通过属性指定我想要返回的枚举值,而不必每次都像这样编写转换代码(特别是因为第三方API在命名规则上相当不一致)。但还是谢谢! - Whit Waldo

0

我认为你的ConConvert()实现不正确。 应该是:

public override bool CanConvert(Type objectType)
{
    return objectType == typeof(Animals);
}

尝试将其从字符串翻转为动物,但仍然返回“BlackBear”,而不是“black_bear”。但还是谢谢! - Whit Waldo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接