简体   繁体   English

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

[英]Deserialize false as null (System.Text.Json)

I'm using an API which for some reason uses false where it should be using null.我正在使用一个 API,它出于某种原因在应该使用 null 的地方使用了 false。 I can't figure out how to deserialize this properly.我不知道如何正确反序列化。 I tried to create a custom JsonConverter to solve this, but was unable to do so.我试图创建一个自定义 JsonConverter 来解决这个问题,但无法这样做。 I'd like to avoid using the dynamic type.我想避免使用动态类型。 How should I deserialize this?我应该如何反序列化这个?

This is the default response.这是默认响应。

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

Which I am deserializing as following.我正在反序列化如下。

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)

And this is the response when there is no supplier for the product.这是当产品没有供应商时的反应。

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

You can create your custom JsonConverter for the supplier property:您可以为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;
    }
}

Update:更新:

If you are using System.Text.Json , you can try the following custom converter:如果您使用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();
    }
}

When using System.Text.Json , you can implement and register your own JsonConverter<T> for the Supplier property.使用System.Text.Json ,您可以为Supplier属性实现和注册您自己的JsonConverter<T>

There are two ways you can consider implementing your converter, depending on your need (whether you need to use the Supplier object in multiple places OR if you need to use different JsonSerializerOption settings or not).有两种方法可以考虑实现转换器,具体取决于您的需要(是否需要在多个位置使用Supplier对象,或者是否需要使用不同的JsonSerializerOption设置)。

  1. Create a JsonConverter<Supplier> and add the custom logic to handle false .创建一个JsonConverter<Supplier>并添加自定义逻辑来处理false Leave the rest to the Deserialize call.剩下的交给Deserialize调用。 In your implementation, do not pass in options.在您的实现中,不要传入选项。 Then, register this converter within the options.然后,在选项中注册此转换器。 This is the simplest approach.这是最简单的方法。
// 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 stackoverflow
        // 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()}
};
  1. Alternatively, create a JsonConverter<Supplier> and add the custom logic to handle "false" along with the deserialization of the Supplier object.或者,创建一个JsonConverter<Supplier>并添加自定义逻辑来处理“false”以及Supplier对象的反序列化。 In this case, you can register this converter either within the options, or use it as an attribute on the Supplier class itself.在这种情况下,您可以在选项中注册此转换器,也可以将其用作Supplier类本身的属性。 Follow this approach if you need to use custom options settings for deserializing the supplier for some reason (such as case insensitive matching of the property names).如果出于某种原因(例如不区分大小写的属性名称匹配)需要使用自定义选项设置来反序列化供应商,请遵循此方法。
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.

This doc will be useful to you when writing custom converters:在编写自定义转换器时,此文档对您很有用:

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

Here are the relevant API docs:以下是相关的 API 文档:

Here's a working example (for both when the json contains a false supplier and when it contains the actual supplier JSON object in the payload): https://dotnetfiddle.net/XFbXB1这是一个工作示例(对于当 json 包含false供应商以及当它在有效负载中包含实际supplier JSON 对象时): https : //dotnetfiddle.net/XFbXB1

Assuming you are using Json.Net假设您使用的是 Json.Net

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


JsonSerializer.Deserialize<Container>(json,settings)

or try或尝试

var serializeOptions = new JsonSerializerOptions
{
    IgnoreNullValues =false
};

 JsonSerializer.Deserialize<Container>(json,serializeOptions)

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

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