【问题标题】:Automatic conversion of numbers to bools - migrating from Newtonsoft to System.Text.Json将数字自动转换为布尔值 - 从 Newtonsoft 迁移到 System.Text.Json
【发布时间】:2021-08-06 13:31:45
【问题描述】:

我无意中在我的 ASP.NET Core 应用程序中切换到 System.Text.Json,从而在我的 API 中引入了一项重大更改。我有一个客户端正在发送 JSON 文档,并且使用数字 10 代替 truefalse 来表示布尔字段:

// What they're sending.
{ "Active": 1 }

// What they should be sending.
{ "Active": true }

Newtonsoft.Json 库通过将数字转换为布尔值(0 = false,其他所有内容 = true)自动处理此问题,但 System.Text.Json 不这样做;它会引发异常。这意味着我的 API 端点突然停止为发送 10 的愚蠢客户端工作!

我似乎在迁移指南中找不到任何提及。我想将行为恢复到 Newtonsoft 处理它的方式,但我不确定是否有一个标志可以在我看不到的地方启用它,或者我是否必须编写一个自定义转换器。

有人可以帮我恢复像 Newtonsoft 的行为吗?


这里有一些代码来演示这个问题:

using System;

string data = "{ \"Active\": 1 }";

try
{
    OutputState s1 = System.Text.Json.JsonSerializer.Deserialize<OutputState>(data);
    Console.WriteLine($"OutputState 1: {s1.Active}");
}
catch (Exception ex)
{
    Console.WriteLine($"System.Text.Json failed: {ex.Message}");
}

try
{
    OutputState s2 = Newtonsoft.Json.JsonConvert.DeserializeObject<OutputState>(data);
    Console.WriteLine($"OutputState 2: {s2.Active}");
}
catch (Exception ex)
{
    Console.WriteLine($"Newtonsoft.Json failed: {ex.Message}");
}

public record OutputState(bool Active);

也是用于交互式游乐场的 .NET 小提琴:https://dotnetfiddle.net/xgm2u7

【问题讨论】:

标签: c# json asp.net-core system.text.json


【解决方案1】:

您可以创建一个自定义JsonConverter&lt;bool&gt; 来模拟Json.NET 的JsonReader.ReadAsBoolean() 的逻辑:

public class BoolConverter : JsonConverter<bool>
{
    public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options) =>
        writer.WriteBooleanValue(value);
    
    public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
        reader.TokenType switch
        {
            JsonTokenType.True => true,
            JsonTokenType.False => false,
            JsonTokenType.String => bool.TryParse(reader.GetString(), out var b) ? b : throw new JsonException(),
            JsonTokenType.Number => reader.TryGetInt64(out long l) ? Convert.ToBoolean(l) : reader.TryGetDouble(out double d) ? Convert.ToBoolean(d) : false,
            _ => throw new JsonException(),
        };
}

将其添加到JsonSerializerOptions.Converters

var options = new JsonSerializerOptions
{
    Converters = { new BoolConverter() },
};
var s1 = System.Text.Json.JsonSerializer.Deserialize<OutputState>(data, options);

演示小提琴 #1 here.

注意事项:

  • Json.NET 自动尝试使用 bool.TryParse() 将字符串反序列化为 bool,它使用 顺序且不区分大小写的比较进行解析。 Utf8JsonReader.ValueTextEquals("true") 不会模拟 Newtonsoft 的大小写不变性。

  • Json.NET 支持将整数和浮点数反序列化为bool,方法是尝试将值标记解析为longdouble(或decimal,当FloatParseHandling.Decimal 设置时)然后调用@ 987654328@ 结果。转换器模拟此逻辑。

  • 在某些边界条件下,我的 BoolConverter&lt;bool&gt; 和 Json.NET 的行为可能不同,尤其是在溢出或舍入接近零的情况下。

  • 如果您还需要支持将数字反序列化为 bool?,请将 NullableConverterFactorythis answer 抓取到 How to deserialize an empty string to a null value for all `Nullable<T>` value types using System.Text.Json? 并将其添加到转换器,如下所示:

     var options = new JsonSerializerOptions
     {
         Converters = { new BoolConverter(), new NullableConverterFactory() },
     };
    

    这个转换器工厂还修复了从 Newtonsoft 到 System.Text.Json 的另一个模糊的重大更改,即前者会将空 JSON 字符串 "" 反序列化为任何 Nullable&lt;T&gt;,但后者不会。

    演示小提琴#2 here.

【讨论】:

  • 或者,您可以指定一个转换器来使用属性反序列化特定属性:[JsonConverter(typeof(BoolConverter))]bool Active { get;set; }。然后你就照常打电话给Deserialize
猜你喜欢
  • 2023-01-12
  • 1970-01-01
  • 2017-03-07
  • 2021-06-30
  • 2019-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多