Intro
System.Text.Json
作为现在 .NET 默认提供的高性能 JSON 序列化器,对于一些比较特殊类型支持的并不太好,业务需求中总是有各种各样的需要,很多时候就需要用到自定义 Converter ,对于微软新出的 DateOnly
/TimeOnly
也是需要自定义 Converter 来支持的
Sample
遇到一个(伪)需求,一个 Id
属性可能是字符串也可能是整型数字,举个栗子,
{"Id": 1, "Name": "Test"}
{"Id": "这是一个 Id", "Name": "Test"}
上面这是两个 JSON,想实现用同一个 Model 来保存结果,应该怎么做呢?
如果 Id
只会是整数或者整数的字符串,那么我们就可以用 int
来表示,System.Text.Json
从 5.0 开始支持解析带引号的数字,也就是数字的字符串形式可以参考:https://github.com/dotnet/runtime/issues/30255,只需要配置 JsonNumberHandling
, 在 ASP.NET Core 中默认是启用的,是可以把 "1"
反序列化成一个 int
类型的但是我们的示例中的 Id
是可能不是数字的,转成数字可能会失败的,所以想要把它当作 string
来处理,最后 model 是这样的
public record TestModel{ public string Id { get; init; } = default!; public string? Name { get; set; }}
但是如果是上面第一种形式的 JSON
反序列化时会发生错误,异常如下:
所以还需要自定义一个 Converter 来支持将数字转换成一个字符串,Converter 实现如下, 属性类型是什么,泛型类型就应该是什么
public class StringOrIntConverter : JsonConverter<string>{ public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Number) { return reader.GetInt32().ToString(); } return reader.GetString(); }
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) { writer.WriteStringValue(value); }}
使用 Converter 的方式有两种,一种是在某个属性上添加 JsonConverter
来使用,另一种是作为全局 Converter 来使用,直接配置 JsonSerializerOptions
中的 Converter
属性使用 Converter 示例:
public record TestModel{ [JsonConverter(typeof(StringOrIntConverter))] public string Id { get; init; } = default!; public string? Name { get; set; }}
配置 JsonSerializerOptions
示例:
JsonSerializer.Deserialize<TestModel>(node.ToJsonString(), new JsonSerializerOptions { Converters = { new StringOrIntConverter() } });
这样我们就可以支持从一个 int
到 string
的转换了,完整示例如下:
var model = new TestModel{ Id = "123", Name = "456"};var jsonString = JsonSerializer.Serialize(model);WriteLine(jsonString);var node = JsonNode.Parse(jsonString);ArgumentNullException.ThrowIfNull(node, nameof(node));node["Id"] = 123;var newJsonString = node.ToJsonString();WriteLine(newJsonString);var newModel = JsonSerializer.Deserialize<TestModel>(newJsonString);WriteLine(model == newModel);
node["Name"] = 345;WriteLine(JsonSerializer.Deserialize<TestModel>(node.ToJsonString(), new JsonSerializerOptions{ Converters = { new StringOrIntConverter() }})?.Name);
输出结果如下:
output
More
可能你会问为什么不直接用 object
,如果使用 object
的话,上面的 Equals
判断就要改写了,需要自己重新实现比较逻辑,而用 string
就不需要了 希望上面自定义 Converter 的代码对你有所帮助~