将 false 反序列化为 null (System.Text.Json)

Posted

技术标签:

【中文标题】将 false 反序列化为 null (System.Text.Json)【英文标题】:Deserialize false as null (System.Text.Json) 【发布时间】:2020-06-23 08:11:39 【问题描述】:

我正在使用一个 API,由于某种原因,它在应该使用 null 的地方使用了 false。我不知道如何正确反序列化它。我试图创建一个自定义 JsonConverter 来解决这个问题,但无法这样做。我想避免使用动态类型。我应该如何反序列化这个?

这是默认响应。


    "products": [
        
            "id": 123456789,
            "supplier": 
                "id": 123456,
                "title": "abc"
            
        
    ]

我正在反序列化如下。

public class Container

    public Product[] products  get; set; 


public class Product

    public ulong id  get; set; 
    public Supplier supplier  get; set; 


public class Supplier

    public ulong id  get; set; 
    public string title  get; set; 


JsonSerializer.Deserialize<Container>(json)

这是当产品没有供应商时的响应。


    "products": [
        
            "id": 123456789,
            "supplier": false
        
    ]

【问题讨论】:

为了做到这一点,这里的问题是API端点返回false,什么时候应该返回null,或者你不能反序列化序列化的对象? @vasilisdmr 问题是我无法反序列化序列化的对象。很遗憾,我无法更改 API 的行为。 查看以下来自 Microsoft 文档的文章,其中说“不支持反序列化到没有无参数构造函数的引用类型”docs.microsoft.com/en-us/dotnet/standard/serialization/… @Pete,你能用我下面的建议解决你的问题吗?***.com/a/60642484/12509023 【参考方案1】:

您可以为supplier 属性创建自定义JsonConverter

public class Product

    public ulong id  get; set; 

    [JsonConverter(typeof(SupplierConverter))]
    public Supplier supplier  get; set; 


public class SupplierConverter : JsonConverter

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    
        throw new NotImplementedException();
    

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    
       if (reader.TokenType == JsonToken.Boolean)
       
           if ((bool)reader.Value == false)
               return null;
       

       return serializer.Deserialize(reader, objectType);
    

    public override bool CanConvert(Type objectType)
    
        return false;
    

更新:

如果你使用System.Text.Json,可以试试下面的自定义转换器:

public class SupplierConverter : JsonConverter<Supplier>

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

        if (options.GetConverter(typeof(JsonElement)) is JsonConverter<JsonElement> converter)
        
            var json = converter.Read(ref reader, typeToConvert, options).GetRawText();

            return JsonSerializer.Deserialize<Supplier>(json);
        

        throw new JsonException();
    

    public override void Write(Utf8JsonWriter writer, Supplier value, JsonSerializerOptions options)
    
        throw new NotImplementedException();
    

【讨论】:

这似乎适用于 Json.NET。我正在使用 System.Text.Json @tzrm,您的示例转换器将按照其编写方式进行堆栈溢出,并且正在做一些不必要的工作 (dotnetfiddle.net/1yqrZ5)。您应该通过选项注册转换器,然后不要在转换器中传递选项,或者在转换器中完全处理供应商转换。以下是您应该考虑编写和使用它的两种方式:dotnetfiddle.net/XFbXB1【参考方案2】:

使用System.Text.Json 时,您可以为Supplier 属性实现并注册自己的JsonConverter&lt;T&gt;

您可以考虑通过两种方式实现转换器,具体取决于您的需要(是否需要在多个位置使用 Supplier 对象,或者是否需要使用不同的 JsonSerializerOption 设置)。

    创建一个JsonConverter&lt;Supplier&gt; 并添加自定义逻辑来处理false。其余的留给Deserialize 电话。在您的实现中,不要传入选项。然后,在选项中注册此转换器。这是最简单的方法。
// Don't register this converter using an attribute on the Supplier class 
// since you are calling Deserialize on this type directly within the converter.
public class SupplierConverter : JsonConverter<Supplier>

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

        // Skip passing options here to avoid ***
        // This approach won't work if you have other options that need to be honored
        // when deserializing Supplier.
        return JsonSerializer.Deserialize<Supplier>(ref reader);
    

    public override void Write(
        Utf8JsonWriter writer, 
        Supplier value, 
        JsonSerializerOptions options)
    
        throw new NotImplementedException();
    


// Register the converter within options as follows
// and pass the options the JsonSerializer.Deserialize call.
var options = new JsonSerializerOptions

    Converters = new SupplierConverter()
;
    或者,创建JsonConverter&lt;Supplier&gt; 并添加自定义逻辑以处理“false”以及Supplier 对象的反序列化。在这种情况下,您可以在选项中注册此转换器,或将其用作Supplier 类本身的属性。如果出于某种原因(例如属性名称的不区分大小写匹配)需要使用自定义选项设置来反序列化供应商,请遵循此方法。
public class SupplierConverter : JsonConverter<Supplier>

    public override Supplier Read(
        ref Utf8JsonReader reader, 
        Type typeToConvert, 
        JsonSerializerOptions options)
    
        // Put whatever special case condition here. 
        // I added a null token check as well, just in case.
        if (reader.TokenType == JsonTokenType.False 
            || reader.TokenType == JsonTokenType.Null)
        
            return null;
        

        var output = new Supplier();

        // Potentially add other error handling for invalid JSON, if needed.
        while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
        
            if (reader.TokenType == JsonTokenType.PropertyName)
            
                if (reader.ValueTextEquals("id"))
                
                    if (!reader.Read()) throw new JsonException();
                    output.id = reader.GetUInt64();
                
                else if (reader.ValueTextEquals("title"))
                
                    if (!reader.Read()) throw new JsonException();
                    output.title = reader.GetString();
                
            
        

        if (reader.TokenType != JsonTokenType.EndObject)
        
            throw new JsonException();
        

        return output;
    

    public override void Write(
        Utf8JsonWriter writer, 
        Supplier value, 
        JsonSerializerOptions options)
    
        throw new NotImplementedException();
    


// Register the converter within options as follows
// and pass the options the JsonSerializer.Deserialize call.
var options = new JsonSerializerOptions

    Converters = new SupplierConverter()
;

// OR
// Use annotate your Supplier class with
// a JsonConverterAttribute.

此文档对您编写自定义转换器很有用:

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to

以下是相关的 API 文档:

https://docs.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonconverter-1?view=netcore-3.1 https://docs.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonconverterattribute?view=netcore-3.1

这是一个工作示例(当 json 包含 false 供应商时,以及当它在负载中包含实际的 supplier JSON 对象时): https://dotnetfiddle.net/XFbXB1

【讨论】:

【参考方案3】:

假设你使用的是 Json.Net

var settings = new JsonSerializerSettings();
        settings.NullValueHandling = NullValueHandling.Include;
        settings.DefaultValueHandling = DefaultValueHandling.Include;


JsonSerializer.Deserialize<Container>(json,settings)

或者试试

var serializeOptions = new JsonSerializerOptions

    IgnoreNullValues =false
;

 JsonSerializer.Deserialize<Container>(json,serializeOptions)

【讨论】:

以上是关于将 false 反序列化为 null (System.Text.Json)的主要内容,如果未能解决你的问题,请参考以下文章

将嵌套的 JSON 反序列化为 C# 对象

XML 反序列化为类

Moshi 无法将 0、1 反序列化为“布尔值?”

Gson 在反序列化对象时忽略 null

在调用API后,将JSON对象反序列化为C#类[重复]。

使用动态键将json反序列化为C#