JSON.Net默认情况下将枚举序列化为字典中的字符串 - 如何将其序列化为int?

时间:2014-08-08 10:19:49

标签: c# json.net

为什么我的序列化JSON最终为

{"Gender":1,"Dictionary":{"Male":100,"Female":200}}

即。为什么enums序列化为它们的值,但是当它们形成时它们是字典的关键字,它们会被转换为它们的键?

如何让它们成为字典中的内容,为什么这不是默认行为?

我希望以下输出

{"Gender":1,"Dictionary":{"0":100,"1":200}}

感谢

    public void foo()
    {
        var testClass = new TestClass();
        testClass.Gender = Gender.Female;
        testClass.Dictionary.Add(Gender.Male, 100);
        testClass.Dictionary.Add(Gender.Female, 200);

        var serializeObject = JsonConvert.SerializeObject(testClass);

        // serializeObject == {"Gender":1,"Dictionary":{"Male":100,"Female":200}}
    }

    public enum Gender
    {
        Male = 0,
        Female = 1
    }

    public class TestClass
    {
        public Gender Gender { get; set; }
        public IDictionary<Gender, int> Dictionary { get; set; }

        public TestClass()
        {
            this.Dictionary = new Dictionary<Gender, int>();
        }
    }
}

2 个答案:

答案 0 :(得分:7)

Gender枚举用作属性值时序列化为其值的原因,但在用作字典键时序列化为其字符串表示形式如下:

  • 当用作属性值时,JSON.NET序列化程序首先写入属性名称,然后写入属性值。对于您发布的示例,JSON.NET将“Gender”作为属性名称(请注意它写入字符串),而不是尝试解析属性的值。该属性的值是enum类型,JSON.NET处理为Int32并且它写入枚举的数字表示

  • 序列化字典时,键被写为属性名称,因此JSON.NET序列化程序会写入枚举的字符串表示形式。如果您在字典中切换键和值的类型(Dictionary<int, Gender>而不是Dictionary<Gender, int>,则您将验证枚举是否将使用其Int32表示进行序列化。

要使用您发布的示例实现所需,您需要为Dictionary属性编写自定义JsonConverter。像这样:

public class DictionaryConverter : JsonConverter
{

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var dictionary = (Dictionary<Gender, int>) value;

        writer.WriteStartObject();

        foreach (KeyValuePair<Gender, int> pair in dictionary)
        {
            writer.WritePropertyName(((int)pair.Key).ToString());
            writer.WriteValue(pair.Value);
        }

        writer.WriteEndObject();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jObject = JObject.Load(reader);

        var maleValue = int.Parse(jObject[((int) Gender.Male).ToString()].ToString());
        var femaleValue = int.Parse(jObject[((int)Gender.Female).ToString()].ToString());

        (existingValue as Dictionary<Gender, int>).Add(Gender.Male, maleValue);
        (existingValue as Dictionary<Gender, int>).Add(Gender.Female, femaleValue);

        return existingValue;
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof (IDictionary<Gender, int>) == objectType;
    }
}

并在TestClass

中修饰该属性
public class TestClass
{
    public Gender Gender { get; set; }
    [JsonConverter(typeof(DictionaryConverter))]
    public IDictionary<Gender, int> Dictionary { get; set; }

    public TestClass()
    {
        this.Dictionary = new Dictionary<Gender, int>();
    }
}

调用以下行进行序列化时:

var serializeObject = JsonConvert.SerializeObject(testClass);

您将获得所需的输出:

{"Gender":1,"Dictionary":{"0":100,"1":200}}

答案 1 :(得分:1)

我经常发现自己面临这个问题,所以我做了一个 JsonConverter,它可以处理任何以 Enum 类型为键的字典:

public class DictionaryWithEnumKeyConverter<T, U> : JsonConverter where T : Enum
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var dictionary = (Dictionary<T, U>)value;

        writer.WriteStartObject();

        foreach (KeyValuePair<T, U> pair in dictionary)
        {
            writer.WritePropertyName(Convert.ToInt32(pair.Key).ToString());
            serializer.Serialize(writer, pair.Value);
        }

        writer.WriteEndObject();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var result = new Dictionary<T, U>();
        var jObject = JObject.Load(reader);

        foreach (var x in jObject)
        {
            T key = (T) (object) int.Parse(x.Key); // A bit of boxing here but hey
            U value = (U) x.Value.ToObject(typeof(U));
            result.Add(key, value);
        }

        return result;
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(IDictionary<T, U>) == objectType;
    }
}

注意:这不会处理 Dictionnary<Enum, Dictionnary<Enum, T>