【问题标题】:JSON.NET not utilizing TypeConverter for Dictionary keyJSON.NET 未将 TypeConverter 用于 Dictionary 键
【发布时间】:2020-12-26 21:26:57
【问题描述】:

我正在尝试使用JSON.NETSortedDictionary<Time, T> 进行序列化/反序列化,其中Time 是我项目中的自定义类,T 是某种不需要包含在此处的类型。

序列化工作完美。以这本词典为例:

var dictionary = new SortedDictionary<Time, T> {
  [Time.Parse("7:00AM")] = ...,
  [Time.Parse("8:00AM")] = ...,
  [Time.Parse("9:00AM")] = ...
};

Time.Parse() 是一个从字符串输入构造Time 对象的简单方法。)现在,如果我运行JsonConvert.SerializeObject(dictionary, Formatting.Indented),我会得到这样的结果:

{
  "7:00AM": ...,
  "8:00AM": ...,
  "9:00AM": ...
}

这是因为Time 类覆盖了ToString() 方法,所以字典键可以很容易地转换为字符串。

不幸的是,反序列化在这里失败了。鉴于上一个示例的输出,运行 JsonConvert.DeserializeObject&lt;SortedDictionary&lt;Time, T&gt;&gt;(previousOutput) 会产生异常:

Newtonsoft.Json.JsonSerializationException

无法将字符串“7:00AM”转换为字典键类型“[...].Time”。创建一个 TypeConverter 以从字符串转换为键类型对象。 [...]

好的,这是有道理的。毕竟,如果没有 some 类型的转换器,JSON.NET 就无法知道如何处理这个问题。但是,我的 Time 类已经有一个 Parse(string) 方法。因此,我创建了一个名为 TimeConverterTypeConverter 来使用它,如下所示:

public class TimeConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) && base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return value is string s ? Time.Parse(s) : base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        return destinationType == typeof(string) && value is Time t
        ? t.ToString()
        : base.ConvertTo(context, culture, value, destinationType);
    }
}

然后我在Time 类声明中添加了一个TypeConverter 属性:

[TypeConverter(typeof(TimeConverter))]
public class Time : IComparable<Time>
{
   ...
}

但是,这不起作用。仍然抛出完全相同的错误。此外,我尝试在 TimeConverter 方法中放置断点,但它们从未被触发。 JSON.NET 似乎完全忽略了我的转换器。我做错了什么?

【问题讨论】:

    标签: c# json json.net typeconverter


    【解决方案1】:

    如果传入的类型是typeof(string),则需要修改TimeConverter.CanConvertFrom()返回true或者基类可以转换类型:

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }
    

    reference source可以看出,基类的CanConvertFrom()只有在传入类型为typeof(InstanceDescriptor)时才返回true。因此,您的 CanConvertFrom() 在通过 typeof(string) 时返回 false 并且从未调用过转换方法。

    可以在here 找到使用Time 类模型的原始问题演示。

    可以在here 找到一个固定的、工作的TypeConverter 的演示。

    但是请注意,如果您使用的是 .NET Core,则仅在 Json.NET 10.0.1 之后才添加对类型转换器的支持。如果您的目标是 netstandard1.0+ 或 .NET Core,并且使用的是早期版本的 Json.NET,则需要升级。详情请见Json.Net: Serialize/Deserialize property as a value, not as an object

    【讨论】:

    • 我明白你的意思,我已经更改了我的代码,但由于某种原因它仍然无法正常工作。 CanConvertFrom() 似乎从未被调用过。但是,您的小提琴似乎确实有效,所以我不确定我在做什么不同。有什么建议吗?
    • @JacobLockard - 那么请edit您的问题包括minimal reproducible example。我必须为Time 创建一个模型,并猜测字典中的T 类型。如果我们可以重现您的问题,您更有可能获得有用的调试帮助。见:How to Ask。我最初必须在这里为您的问题创建自己的minimal reproducible exampledotnetfiddle.net/NicoO1
    • @JacobLockard - 你的实际Time 类是否也实现IConvertible?如果是这样,也许可以看到Newtonsoft.Json - DeserializeObject throws when deserializing custom type: Error converting value “somestring” to type CustomType
    • 好吧,在 DotNetFiddle 上的一个示例运行之后,我最终决定尝试更新我的 Newtonsoft.Json 版本。果然,从 9.* 升级到 12.* 解决了这个问题!谢谢你的帮助!在这种情况下,建议采取什么措施?我应该用我所做的事情创建一个新的答案并将其标记为已接受的答案,还是应该留下问题?
    • @JacobLockard - 即使您从 Json.NET 9 更新到 12,我认为您的转换器也不会工作,除非您让 CanConvertFrom() 返回 true。我失败的小提琴 dotnetfiddle.net/NicoO1 使用 Json.NET 12。但是如果你在 .Net Core 中工作,你很可能需要同时做这两个,因为 Json.NET 不支持类型转换器in .Net Core 直到release 10
    【解决方案2】:

    事实证明,这是两个不同问题的结合:

    1. 正如@dbc 的回答中所解释的,我在TimeConverter.CanConvertFrom() 中使用&amp;&amp; 而不是||
    2. 我使用的是 Json.NET 版本 9,但在 .NET Core 中,直到版本 10 才实现 TypeConverter 支持。

    使用 || 并升级到 Json.NET 12 解决了我的问题。

    【讨论】:

      猜你喜欢
      • 2022-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-07
      • 1970-01-01
      相关资源
      最近更新 更多