总目录
一、什么是 NumberHandling
?
1.1 概述
NumberHandling
是 System.Text.Json 中用于 控制数字序列化和反序列化行为 的特性。它允许开发者在处理 JSON 数据时,灵活地指定如何解析和生成数字(如整数、浮点数)。
1.2 适用场景
NumberHandling
特性通过 [JsonNumberHandling]
属性或 JsonSerializerOptions.NumberHandling
配置来实现,支持以下场景:
- 处理科学计数法:支持 JSON 中的科学计数格式(如
1.23e+10
)。 - 宽松解析:允许将数字解析为字符串,或将字符串解析为数字。
- 严格类型检查:确保 JSON 数字与 C# 类型完全匹配。
- 特殊浮点值:处理
NaN
、Infinity
等非标准数值。
二、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 的 double
或 float
类型。
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
五、注意事项
- 性能影响:
- 启用宽松解析(如
AllowReadingFromString
)会增加解析开销,需权衡性能与灵活性。
- 启用宽松解析(如
- 安全问题:
- 宽松解析可能导致意外的数据转换(如将无效字符串解析为默认值)。
- 优先级:
- 属性级别的
[JsonNumberHandling]
优先于全局JsonSerializerOptions.NumberHandling
。
- 属性级别的
结语
回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
参考资料: