简体   繁体   English

JSON.NET 未将 TypeConverter 用于 Dictionary 键

[英]JSON.NET not utilizing TypeConverter for Dictionary key

I am attempting to use JSON.NET to serialize/deserialize a SortedDictionary<Time, T> , where Time is a custom class in my project, and T is some type that need not be included here.我正在尝试使用JSON.NET来序列化/反序列化SortedDictionary<Time, T> ,其中Time是我项目中的自定义 class ,而T是某种不需要包含在此处的类型。

Serialization works perfectly.序列化工作完美。 Take this dictionary, for example:以这本词典为例:

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

( Time.Parse() is a simple method that constructs Time objects from a string input.) Now, if I run JsonConvert.SerializeObject(dictionary, Formatting.Indented) , I get something like this: Time.Parse()是一个从字符串输入构造Time对象的简单方法。)现在,如果我运行JsonConvert.SerializeObject(dictionary, Formatting.Indented) ,我会得到如下信息:

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

This is because the Time class overrides the ToString() method, so the dictionary keys can easily be converted to strings.这是因为Time class 覆盖了ToString()方法,因此可以轻松地将字典键转换为字符串。

Unfortunately, deserialization fails here.不幸的是,反序列化在这里失败了。 Given the output of the last example, running JsonConvert.DeserializeObject<SortedDictionary<Time, T>>(previousOutput) produces an exception:鉴于最后一个示例的 output,运行JsonConvert.DeserializeObject<SortedDictionary<Time, T>>(previousOutput)会产生异常:

Newtonsoft.Json.JsonSerializationException Newtonsoft.Json.JsonSerializationException

Could not convert string '7:00AM' to dictionary key type '[...].Time'.无法将字符串“7:00AM”转换为字典键类型“[...].Time”。 Create a TypeConverter to convert from the string to the key type object.创建一个 TypeConverter 以将字符串转换为键类型 object。 [...] [...]

Okay, that makes sense.好吧,这是有道理的。 After all, without some kind of converter, there is no way for JSON.NET to know how to handle this.毕竟,如果没有某种转换器,JSON.NET 就无法知道如何处理这个问题。 However, my Time class already has a Parse(string) method.但是,我的Time class 已经有了Parse(string)方法。 So, I created a TypeConverter called TimeConverter that utilizes it, like so:因此,我创建了一个名为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);
    }
}

I then added a TypeConverter attribute to the Time class declaration:然后我在Time class 声明中添加了一个TypeConverter属性:

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

However, this did not work.然而,这并没有奏效。 The exact same error is still thrown.仍然抛出完全相同的错误。 Additionally, I tried placing breakpoints in the TimeConverter methods, but they were never triggered.此外,我尝试在TimeConverter方法中放置断点,但它们从未被触发。 JSON.NET seems to be completely ignoring my converter. JSON.NET 似乎完全忽略了我的转换器。 What am I doing wrong?我究竟做错了什么?

You need to modify TimeConverter.CanConvertFrom() to return true if the incoming type is typeof(string) or the base class can convert the type:如果传入类型是typeof(string)或者基础 class 可以转换类型,则需要修改TimeConverter.CanConvertFrom()以返回 true:

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

As can be seen from the reference source , the base class's CanConvertFrom() only returns true when the incoming type is typeof(InstanceDescriptor) .参考源可以看出,基类的CanConvertFrom()只有在传入类型为typeof(InstanceDescriptor)时才返回true Thus your CanConvertFrom() return false when passed typeof(string) and the conversion methods never got called.因此,您的CanConvertFrom()在传递typeof(string)时返回 false 并且从未调用过转换方法。

A demo of the original problem using a mockup for Time class can be found here .可以在此处找到使用Time class 模型的原始问题的演示。

A demo of the fixed, working TypeConverter can be found here .可以在此处找到固定的工作TypeConverter的演示。

Note however that, if you are using .NET Core, support for type converters was only added as of Json.NET 10.0.1 .但是请注意,如果您使用的是 .NET 内核,则仅从Json.NET 10.0.1开始添加对类型转换器的支持。 If you are targeting netstandard1.0+ or .NET Core and are using an earlier version Json.NET you will need to upgrade.如果您的目标是 netstandard1.0+ 或 .NET Core,并且使用的是早期版本 Json.NET,则需要升级。 See Json.Net: Serialize/Deserialize property as a value, not as an object for details.有关详细信息,请参阅Json.Net:将属性序列化/反序列化为值,而不是 object

It turns out that this was a combination of two different issues:事实证明,这是两个不同问题的结合:

  1. As explained in @dbc's answer, I was using && instead of ||正如@dbc 的回答中所解释的,我使用的是&&而不是|| in TimeConverter.CanConvertFrom() .TimeConverter.CanConvertFrom()中。
  2. I was using version 9 of Json.NET, but in .NET Core , TypeConverter support was not implemented until version 10.我使用的是 Json.NET 的版本 9,但是在 .NET Core中,直到版本 10 才实现TypeConverter支持。

Using ||使用|| and upgrading to Json.NET 12 solved my issue.并升级到 Json.NET 12 解决了我的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM