C# System.Text.Json 中 NumberHandling 使用详解

总目录


一、什么是 NumberHandling

1.1 概述

NumberHandlingSystem.Text.Json 中用于 控制数字序列化和反序列化行为 的特性。它允许开发者在处理 JSON 数据时,灵活地指定如何解析和生成数字(如整数、浮点数)。

1.2 适用场景

NumberHandling 特性通过 [JsonNumberHandling] 属性或 JsonSerializerOptions.NumberHandling 配置来实现,支持以下场景:

  1. 处理科学计数法:支持 JSON 中的科学计数格式(如 1.23e+10)。
  2. 宽松解析:允许将数字解析为字符串,或将字符串解析为数字。
  3. 严格类型检查:确保 JSON 数字与 C# 类型完全匹配。
  4. 特殊浮点值:处理 NaNInfinity 等非标准数值。

二、NumberHandling 枚举

NumberHandling 提供了以下几种模式:

模式作用
Strict严格模式,默认行为,JSON 数字必须与目标类型完全匹配。
AllowReadingFromString允许从 JSON 字符串中读取数字(如 "123"123)。
WriteAsString将数字序列化为 JSON 字符串(如 123"123")。
AllowNamedFloatingPointLiterals支持解析特殊浮点值(如 "NaN", "Infinity", "-Infinity")。

三、使用方式

3.1 通过 [JsonNumberHandling] 特性

可以为属性或类指定 NumberHandling 模式。

示例1:允许从字符串解析数字

public class Product
{
    [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
    public int Id { get; set; }

    [JsonNumberHandling(JsonNumberHandling.WriteAsString)]
    public decimal Price { get; set; }
    public decimal Price2 { get; set; }
}

class Program
{
    static void Main()
    {
        var product = new Product() { Id = 111, Price = 49.99m, Price2 = 50.24m };
        // 序列化
        string serializedJson = JsonSerializer.Serialize(product);
        Console.WriteLine(serializedJson);
        // 输出:{"Id":111,"Price":"49.99","Price2":50.24}
        // Price 属性 序列化时 序列化为 string 类型

        // 反序列化
        var json = "{\"Id\":\"123\",\"Price\":49.99,\"Price2\":50.24}";
        product = JsonSerializer.Deserialize<Product>(json);
        Console.WriteLine(product.Id);      // 输出:123
        Console.WriteLine(product.Price);   // 输出:49.99
    }
}

示例2:支持解析特殊浮点值

public class Product
{
    public int Id { get; set; }

    [JsonNumberHandling(JsonNumberHandling.AllowNamedFloatingPointLiterals)]
    public double Price { get; set; }
}

class Program
{
    static void Main()
    {
        var product = new Product() { Id = 111, Price = double.NaN };
        // 序列化
        string serializedJson = JsonSerializer.Serialize(product);
        Console.WriteLine(serializedJson);
        // 输出:{"Id":111,"Price":"NaN"}

        // 反序列化
        product = JsonSerializer.Deserialize<Product>(serializedJson);
        Console.WriteLine(product.Id);      // 输出:111
        Console.WriteLine(product.Price);   // 输出:NaN
    }
}

在上例中,当我们去掉 JsonNumberHandling.AllowNamedFloatingPointLiterals 特性,运行就会报错。

3.2 通过 JsonSerializerOptions 配置全局行为

可以在 JsonSerializerOptions 中设置全局的 NumberHandling 模式。

示例:支持科学计数法

var options = new JsonSerializerOptions
{
    NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.AllowNamedFloatingPointLiterals
};

// 示例数据
var json = "[\"123\", \"1.23e+10\", \"NaN\"]";

// 反序列化
var numbers = JsonSerializer.Deserialize<List<double>>(json, options);
Console.WriteLine(numbers[0]); // 输出:123
Console.WriteLine(numbers[1]); // 输出:12300000000
Console.WriteLine(numbers[2]); // 输出:NaN

// 序列化
var data = new List<double> { 123, double.NaN };
string serializedJson = JsonSerializer.Serialize(data, options);
Console.WriteLine(serializedJson); // 输出:["123","NaN"]

四、常见使用场景

4.1 处理科学计数法

JSON 数据中可能包含科学计数法表示的数字(如 1.23e+10),需要正确解析为 .NET 类型。

var json = "[\"1.23e+10\"]";

var options = new JsonSerializerOptions
{
    NumberHandling = JsonNumberHandling.AllowReadingFromString
};

var numbers = JsonSerializer.Deserialize<List<double>>(json, options);
Console.WriteLine(numbers[0]); // 输出:12300000000

4.2 将数字序列化为字符串

某些 API 要求数字以字符串形式传递,例如金融系统中的金额字段。

public class Transaction
{
    [JsonNumberHandling(JsonNumberHandling.WriteAsString)]
    public decimal Amount { get; set; }
}

var transaction = new Transaction { Amount = 12345.67m };
string json = JsonSerializer.Serialize(transaction);
Console.WriteLine(json); // 输出:{"Amount":"12345.67"}

4.3 支持特殊浮点值

JSON 中可能包含特殊浮点值(如 "NaN", "Infinity"),需要正确解析为 .NET 的 doublefloat 类型。

var json = "[\"NaN\", \"Infinity\", \"-Infinity\"]";

var options = new JsonSerializerOptions
{
    NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals
};

var numbers = JsonSerializer.Deserialize<List<double>>(json, options);
Console.WriteLine(numbers[0]); // 输出:NaN
Console.WriteLine(numbers[1]); // 输出:Infinity
Console.WriteLine(numbers[2]); // 输出:-Infinity

4.4 宽松解析:允许数字与字符串互转

某些场景下,JSON 数据可能将数字存储为字符串,或者将字符串存储为数字。

var json = "{\"Id\":\"123\",\"Price\":49.99}";

var options = new JsonSerializerOptions
{
    NumberHandling = JsonNumberHandling.AllowReadingFromString
};

var product = JsonSerializer.Deserialize<Product>(json, options);
Console.WriteLine(product.Id); // 输出:123

五、注意事项

  1. 性能影响
    • 启用宽松解析(如 AllowReadingFromString)会增加解析开销,需权衡性能与灵活性。
  2. 安全问题
    • 宽松解析可能导致意外的数据转换(如将无效字符串解析为默认值)。
  3. 优先级
    • 属性级别的 [JsonNumberHandling] 优先于全局 JsonSerializerOptions.NumberHandling

结语

回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考资料:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲤籽鲲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值