【问题标题】:Json.NET - ContractResolver to serialize dictionary with non-primitive keysJson.NET - ContractResolver 用非原始键序列化字典
【发布时间】:2017-08-15 14:39:33
【问题描述】:

我有一些带有本地化文本的 .NET 类;即英文文本,对应的西班牙语文本等。我​​们有一个类似这样的 Locale 类:

class Locale
{
   int Id;
   string Abbreviation; // e.g. "en"
   string Name;         // e.g. "English"

   static Locale FromAbbreviation(string abbreviation);
}

本地化文本存储在 IDictionary 属性中,例如

class Document
{
   IDictionary<Locale, string> Content;
}

当序列化为 JSON 时,我希望它以语言环境缩写作为键,因此序列化的 Document 对象看起来像这样:

{
   "content": {
      "en": "English content",
      "es": "Spanish content"
   }
}

我需要一个 ContractResolver,它将 IDictionary&lt;Locale, string&gt; 对象转换为 Dictionary&lt;string, string&gt; 对象,在序列化期间使用 Locale.Abbreviation 属性作为键,并在反序列化时调用 Locale.FromAbbreviation() 以将键转换回 Locale对象。

我查看了 JSON.NET 文档和各种 Stackoverflow 问题,似乎没有一种简单的方法可以做到这一点(至少我找不到)。我确实找到了看起来像 straightforward way to do the same thing using a TypeConverter attribute 的东西,但我不想从我的域类中依赖 Json.NET。有没有使用 ContractResolver 的合理方法?

【问题讨论】:

    标签: c# json serialization json.net


    【解决方案1】:

    您在这里并不需要ContractResolver。您可以使用自定义 JsonConverter 来处理从 Dictionary&lt;Locale, string&gt; 到所需 JSON 的转换并返回,而无需在模型类中使用属​​性。只要转换器的CanConvert 方法编码正确以识别转换器处理的字典类型,那么您只需将转换器添加到序列化器设置中,Json.Net 就会找到并适当地使用它。

    这是转换器的外观:

    class LocaleDictionaryConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(IDictionary<Locale, string>).IsAssignableFrom(objectType);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject obj = JObject.Load(reader);
            IDictionary<Locale, string> dict = (IDictionary<Locale, string>)existingValue ?? new Dictionary<Locale, string>();
            foreach (var prop in obj.Properties())
            {
                dict.Add(Locale.FromAbbreviation(prop.Name), (string)prop.Value);
            }
            return dict;
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            IDictionary<Locale, string> dict = (IDictionary<Locale, string>)value;
            JObject obj = new JObject();
            foreach (var kvp in dict)
            {
                obj.Add(kvp.Key.Abbreviation, kvp.Value);
            }
            obj.WriteTo(writer);
        }
    }
    

    下面是你将如何使用它来序列化:

    var settings = new JsonSerializerSettings();
    settings.Converters.Add(new LocaleDictionaryConverter());
    settings.Formatting = Formatting.Indented;
    
    string json = JsonConvert.SerializeObject(document, settings);
    

    并反序列化:

    // same settings as above
    var document = JsonConvert.DeserializeObject<Document>(json, settings);
    

    这是一个演示:https://dotnetfiddle.net/f1rXl2

    【讨论】:

    • 这看起来很完美;我会试一试。感谢您的快速回复!
    • 我需要做的唯一改变是在 ReadJson() 中使用 existingValue 参数,因为这些类型的字典引用在我们的代码中往往是只读的(参见stackoverflow.com/a/27904050/2144):IDictionary dict = (IDictionary)existingValue ??新字典();
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-10
    • 2015-05-22
    • 1970-01-01
    • 2014-02-19
    相关资源
    最近更新 更多