返回类型中引用类型的可空性与重写的成员不匹配



我使用的是一个返回JSON的API,其中一个值可以是false或对象。为了处理这个问题,我创建了一个自定义的JsonConverter<T>

internal class JsonFalseOrObjectConverter<T> : JsonConverter<T> where T : class
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.False)
{
return null;
}
else
{
return JsonSerializer.Deserialize<T>(ref reader);
}
}
}

问题是我得到了以下编译器错误:

可能返回空引用。

我可以将返回的类型设置为T?,但随后我会得到错误:

返回类型中引用类型的可空性与重写成员不匹配。

如何修复?

您已经声明泛型类型是(不可为null的(T,但您返回的是null。这显然是站不住脚的。

如果你不在乎的话,你需要让你的转换器实现JsonConverter<T?>或者使用零容忍运算符。

internal class JsonFalseOrObjectConverter<T> : JsonConverter<T?> where T : class
{
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
...
}
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
{
...
}
}

public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.False)
{
return null!;
}
...
}

最简单的解决方案是return null!:

#nullable enable
internal class JsonFalseOrObjectConverter<T> : JsonConverter<T> where T : class
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.False)
{
return null!;
}
else
{
return JsonSerializer.Deserialize<T>(ref reader);
}
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options){}
}    

引用类可以为null的,因此编译器在遇到T?时只使用T

一个更好的选择是创建类似于F#的option类型的东西,如果设置了值,则包含Some值,如果值为false,则包含None。通过使Option成为一个结构,即使属性丢失或为空,我们也会获得默认的None值:

readonly struct Option<T> 
{
public readonly T Value {get;}
public readonly bool IsSome {get;}
public readonly bool IsNone =>!IsSome;
public Option(T value)=>(Value,IsSome)=(value,true);    
public void Deconstruct(out T value)=>(value)=(Value);
}
//Convenience methods, similar to F#'s Option module
static class Option
{
public static Option<T> Some<T>(T value)=>new Option<T>(value);    
public static Option<T> None<T>()=>default;
}

如果遇到false,解串器可以返回None()default


internal class JsonFalseOrObjectConverter<T> : JsonConverter<Option<T>> where T : class
{
public override Option<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.False)
{
return Option.None<T>(); // or default
}
else
{
return Option.Some(JsonSerializer.Deserialize<T>(ref reader));
}
}
public override void Write(Utf8JsonWriter writer, Option<T> value, JsonSerializerOptions options)
{
switch (value)
{
case Option<T> (_    ,false) :
JsonSerializer.Serialize(writer,false,options);
break;
case Option<T> (var v,true) :
JsonSerializer.Serialize(writer,v,options);
break;
}
}
}    

Write方法展示了如何使用模式匹配来处理Option<T>

使用这个序列化程序,可以生成以下类:


class Category
{
public string Name{get;set;}
}

class Product
{
public string Name{get;set;}
public Option<Category> Category {get;set;}
}

可以使用为缺失类别生成的false进行序列化:

var serializerOptions = new JsonSerializerOptions
{ 
Converters = { new JsonFalseOrObjectConverter<Category>() }
};
var product1=new Product{Name="A"};
var json=JsonSerializer.Serialize(product1,serializerOptions);

返回:

{"Name":"A","Category":false}

反序列化此字符串将返回一个Product,其Category是不带值的Option<Category>

var product2=JsonSerializer.Deserialize<Product>(json,serializerOptions);
Debug.Assert(product2.Category.IsNone);

模式匹配表达式可以用于提取和使用类别的属性,如果它有值,例如:

string category=product2.Category switch { Option<Category> (_    ,false) =>"No Category",
Option<Category> (var v,true)  => v.Name};

if(product2.Category is Option<Category>(var cat,true))
{
Console.WriteLine(cat.Name);
}

最新更新