定义类型序列化转换器 当我们需要对类型使用一些不同的序列化和反序列化方式时,我们可以自定义一些转换器,方式就是实现JsonConverter 抽象类。其实这是遵循基本模式的定义,还有一种基于工厂模式的定义,更多详情见如何在 .NET 中编写用于 JSON 序列化(封送)的自定义转换器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public partial class DateOnlyNullableConverter : JsonConverter <DateOnly ?>{ [GeneratedRegex("(\\d{4})年(\\d{1,2})月(\\d{1,2})日" ) ] private static partial Regex DateRegex () ; public override DateOnly? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var dateString = reader.GetString(); if (string .IsNullOrWhiteSpace(dateString)) return null ; var m = DateRegex().Match(dateString); if (m.Groups.Count == 4 ) { return new DateOnly(int .Parse(m.Groups[1 ].Value), int .Parse(m.Groups[2 ].Value), int .Parse(m.Groups[3 ].Value)); } return null ; } public override void Write (Utf8JsonWriter writer, DateOnly? value , JsonSerializerOptions options ) => writer.WriteStringValue(value ?.ToString("yyyy年MM月dd日" )); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public partial class TimeOnlyNullableConverter : JsonConverter <TimeOnly ?>{ [GeneratedRegex("(\\d{1,2})小时(\\d{1,2})分(\\d{1,2})秒" ) ] private static partial Regex TimeRegex () ; public override TimeOnly? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var timeString = reader.GetString(); if (string .IsNullOrWhiteSpace(timeString)) return null ; var m = TimeRegex().Match(timeString); if (m.Groups.Count == 4 ) { return new TimeOnly(int .Parse(m.Groups[1 ].Value), int .Parse(m.Groups[2 ].Value), int .Parse(m.Groups[3 ].Value)); } return null ; } public override void Write (Utf8JsonWriter writer, TimeOnly? value , JsonSerializerOptions options ) => writer.WriteStringValue(value ?.ToString("HH小时mm分ss秒" )); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 public readonly struct Point{ public decimal Lng { get ; init ; } public decimal Lat { get ; init ; } public Point (decimal lng, decimal lat ) => (Lng, Lat) = (lng, lat); public override string ? ToString() => $"{Lng} ,{Lat} " ; } public class PointConvert : JsonConverter <Point >{ public override Point? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var lnglatString = reader.GetString(); if (string .IsNullOrWhiteSpace(lnglatString)) return null ; var lnglat = lnglatString?.Split(new char [] { ',' , ',' }); if (lnglat?.Length == 2 && decimal .TryParse(lnglat[0 ], out var lng) && decimal .TryParse(lnglat[1 ], out var lat)) { return new Point(lng, lat); } throw new Exception("Point anomaly!" ); } public override void Write (Utf8JsonWriter writer, Point? value , JsonSerializerOptions options ) { writer.WriteStringValue(value ?.ToString()); } } public class PointNullableConvert : JsonConverter <Point ?>{ public override Point? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var lnglatString = reader.GetString(); if (string .IsNullOrWhiteSpace(lnglatString)) return null ; var lnglat = lnglatString?.Split(new char [] { ',' , ',' }); if (lnglat?.Length == 2 && decimal .TryParse(lnglat[0 ], out var lng) && decimal .TryParse(lnglat[1 ], out var lat)) { return new Point(lng, lat); } throw new Exception("Point anomaly!" ); } public override void Write (Utf8JsonWriter writer, Point? value , JsonSerializerOptions options ) { writer.WriteStringValue(value ?.ToString()); } }
如何实现对象内的string字段自动去掉前后空格? 在前端传参数的时候,尤其是从输入框中取得的参数,很有可能用户从其他地方复制内容过来时前后会带一些空格,如果没有做Trim前后空格的处理那么很有可能会查询不出数据,如果后端每一个都去Trim前后空格又有点麻烦,而且有可能还会忘,所以可以利用序列化转换器来统一处理。
1 2 3 4 5 6 7 8 9 10 11 12 public class StringConvert : JsonConverter <string >{ public override string ? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return reader.GetString()?.Trim(); } public override void Write (Utf8JsonWriter writer, string value , JsonSerializerOptions options ) { writer.WriteStringValue(value ); } }
注册自定义转换器的三种方式 注册到转换器集合中 ASP.NET Core开始时,调用IMvcBuilder的AddJsonOptions扩展方法将转换器注册到Json的转换器集合中:
1 2 3 4 5 6 7 8 builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new DateOnlyNullableConverter()); options.JsonSerializerOptions.Converters.Add(new TimeOnlyNullableConverter()); options.JsonSerializerOptions.Converters.Add(new PointConvert()); options.JsonSerializerOptions.Converters.Add(new PointNullableConvert()); });
直接使用JsonSerializer进行序列化时,也是需要添加到Json的转换器集合中:
1 2 3 4 5 6 7 8 9 10 11 var serializeOptions = new JsonSerializerOptions{ Converters = { new DateOnlyNullableConverter(), new TimeOnlyNullableConverter(), new PointConvert(), new StringConvert() } }; JsonSerializer.Serialize(obj, serializeOptions);
在属性上添加特性[JsonConverter] 1 2 3 4 5 6 public class People { [JsonConverter(typeof(StringConvert)) ] public string ? Name { get ; set ; } }
.net 7时支持了泛型特性这个功能,所以扩展了一个新特性,使之支持[JsonConverter]
1 2 3 4 5 6 7 8 9 10 11 public class JsonConverterAttribute <T > : JsonConverterAttribute { public JsonConverterAttribute (): base (typeof (T )) { } } public class People { [JsonConverter<StringConvert> ] public string ? Name { get ; set ; } }
在自定义值类型的类或结构上添加特性[JsonConverter] 1 2 3 4 5 6 7 8 [JsonConverter(typeof(PointNullableConvert)) ] public readonly struct Point{ public decimal Lng { get ; init ; } public decimal Lat { get ; init ; } public Point (decimal lng, decimal lat ) => (Lng, Lat) = (lng, lat); public override string ? ToString() => $"{Lng} ,{Lat} " ; }
转化器注册优先级 从高到低分别是:
应用于属性的 [JsonConverter]。
向 Converters 集合添加的转换器。
应用于自定义值类型或 POCO 的 [JsonConverter]。