JsonConverter<T>.Read中'typeToConvert'的作用是什么?

What is the purpose of 'typeToConvert' in JsonConverter<T>.Read?

我一直在我的所有新项目中采用新的 System.Text.Json 库。我知道如何使用它,它看起来与 Newtonsoft api.

非常相似

Newtonsoft中的JsonConverter class是非泛型的,一个转换器可以处理多种类型,所以每次读取一个值时,都必须指定类型您要读取的值。然而,System.Text.Json命名空间中的JsonConverter<T>是通用的,而return值是强类型的,所以你应该在编译时总是知道什么您要转换的类型。如果您在编译时不知道类型,则必须使用 JsonConverterFactory 代替。

为什么 Read 方法有签名 public T Read(reader, typeToConvert, options)typeToConvert 什么时候会比 T 更具体?

参考源码可以看出,JsonConverter<T>.CanConvert(Type typeToConvert)没有封号:

public override bool CanConvert(Type typeToConvert)
{
    return typeToConvert == typeof(T);
}

因此,应用程序可以覆盖此方法,并且 return 对于可分配给 T 的某些类型为真,例如:

public override bool CanConvert(Type typeToConvert) => typeof(T).IsAssignableFrom(typeToConvert);

在这种情况下,在 Read (ref reader, Type typeToConvert, options) 中,typeToConvert 将是被反序列化的值的 声明类型 T 是输入转换器可以转换。您将需要使用反射(例如 (T)Activator.CreateInstance(typeToConvert))来构造被反序列化的实例。

举个具体的例子,假设你正在处理来自 and need to deserialize a JSON value that is sometimes an array and sometimes a single item to a Collection<string>的情况。为此,您定义以下 JsonConverter<Collection<TValue>>:

public class SingleOrArrayCollectionConverter<TItem> : JsonConverter<Collection<TItem>>
{
    public SingleOrArrayCollectionConverter() : this(true) { }
    public SingleOrArrayCollectionConverter(bool canWrite) => CanWrite = canWrite;
    public bool CanWrite { get; }
    
    public override bool CanConvert(Type typeToConvert) => typeof(Collection<TItem>).IsAssignableFrom(typeToConvert);

    public override Collection<TItem> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var list = typeToConvert == typeof(Collection<TItem>) ? new Collection<TItem>() : (Collection<TItem>)Activator.CreateInstance(typeToConvert);
        switch (reader.TokenType)
        {
            case JsonTokenType.StartArray:
                while (reader.Read())
                {
                    if (reader.TokenType == JsonTokenType.EndArray)
                        break;
                    list.Add(JsonSerializer.Deserialize<TItem>(ref reader, options));
                }
                break;
            default:
                list.Add(JsonSerializer.Deserialize<TItem>(ref reader, options));
                break;
        }
        return list;
    }
    
    public override void Write(Utf8JsonWriter writer, Collection<TItem> value, JsonSerializerOptions options)
    {
        if (CanWrite && value.Count == 1)
            JsonSerializer.Serialize(writer, value.First(), options);
        else
        {
            writer.WriteStartArray();
            foreach (var item in value)
                JsonSerializer.Serialize(writer, item, options);
            writer.WriteEndArray();
        }
    }
}

然后转换器可用于反序列化为 Collection<string> 的任何子 class,例如 ObservableCollection<string>。如果你有一些 JSON 可能是单个字符串或字符串数​​组,你可以这样做:

var json = @"""hello"""; // A JSON string primitive "hello"
var options = new JsonSerializerOptions
{
    Converters = { new SingleOrArrayCollectionConverter<string>() },
};
var collection = JsonSerializer.Deserialize<ObservableCollection<string>>(json, options);

备注:

  • 如果您覆盖 CanConvert,您有责任确保它仅 returns true 可分配给 T 的类型。在类型不兼容的情况下,System.Text.Json 将抛出一个 InvalidOperationException,例如:

     System.InvalidOperationException: The converter 'TConverter' is not compatible with the type 'TActualValue'.
    
  • 对于包含开放泛型的类型层次结构,您必须使用 factory pattern to create specific JsonConverter<T> instances for each concrete type to be deserialized. 显示了一个这样的工厂,它为所有 [=31] 生成一个 SingleOrArrayConverter<TItem> 转换器=]类型。

    对于没有开放类型的类型层次结构,您也可以选择使用工厂模式。例如,工厂模式将允许您仅为具有 public 无参数构造函数(即满足 where T : new() 约束)的某种类型 T 的子类型制造转换器。但是对于没有开放泛型的 class 层次结构,工厂模式是可选的而不是必需的。

  • 有关覆盖 CanConvert 和反序列化多态类型层次结构的 JsonConverter<T> 的示例,请参阅 to by ahsonkhan

演示 fiddle here.