我正在写一个 custom System.Text.Json.JsonConverter将旧数据模型升级到新版本。我已覆盖 Read()并实现了必要的后处理。但是,我根本不需要在 Write()中做任何自定义操作。方法。如果我根本没有转换器,如何自动生成默认序列化?显然我可以使用不同的 JsonSerializerOptions用于反序列化和序列化,但是我的框架并没有直接为每个提供不同的选项。
下面是一个简化的例子。假设我以前有以下数据模型:

  1. public record Person(string Name);

我已经升级到

  1. public record Person(string FirstName, string LastName);

我写了一个转换器如下:

  1. public sealed class PersonConverter : JsonConverter<Person>
  2. {
  3. record PersonDTO(string FirstName, string LastName, string Name); // A DTO with both the old and new properties.
  4. public override Person Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
  5. {
  6. var dto = JsonSerializer.Deserialize<PersonDTO>(ref reader, options);
  7. var oldNames = dto?.Name?.Split(' ', StringSplitOptions.RemoveEmptyEntries) ?? Enumerable.Empty<string>();
  8. return new Person(dto.FirstName ?? oldNames.FirstOrDefault(), dto.LastName ?? oldNames.LastOrDefault());
  9. }
  10. public override void Write(Utf8JsonWriter writer, Person person, JsonSerializerOptions options)
  11. => // What do I do here? I want to preserve other options such as options.PropertyNamingPolicy, which are lost by the following call
  12. JsonSerializer.Serialize(writer, person);
  13. }

和往返

  1. var options = new JsonSerializerOptions
  2. {
  3. PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
  4. Converters = { new PersonConverter() },
  5. };
  6. var person = JsonSerializer.Deserialize<Person>(json, options);
  7. var json2 = JsonSerializer.Serialize(person, options);

那么结果是{"FirstName":"FirstName","LastName":"LastName"} -- 即序列化期间的驼峰式 shell 丢失。但是如果我在编写时通过递归调用传递选项

  1. public override void Write(Utf8JsonWriter writer, Person person, JsonSerializerOptions options)
  2. => // What do I do here? I want to preserve other options such as options.PropertyNamingPolicy, which are lost by the following call
  3. JsonSerializer.Serialize(writer, person, options);

然后序列化因堆栈溢出而失败。
如何获得忽略自定义转换器的精确默认序列化?没有等效于 Json.NET 的 JsonConverter.CanWrite属性(property)。
演示 fiddle here .

最佳答案

如 docs 中所述, 转换器的选择具有以下优先级:

  • [JsonConverter] applied to a property.
  • A converter added to the Converters collection.
  • [JsonConverter] applied to a custom value type or POCO.

每个案例都需要单独处理。

  • 如果您有 [JsonConverter]应用于属性。然后只需调用 JsonSerializer.Serialize(writer, person, options);将生成默认序列化。
  • 如果您将 A 转换器添加到 Converters集合。然后在 Write() 里面(或 Read()) 方法,可以复制传入的 options使用 JsonSerializerOptions copy constructor , 从副本的 Converters中删除转换器列表,并将修改后的副本传递给 JsonSerializer.Serialize<T>(Utf8JsonWriter, T, JsonSerializerOptions);这在 .NET Core 3.x 中无法轻松完成,因为该版本中不存在复制构造函数。临时修改Converters移除转换器的传入选项的集合不是线程安全的,因此不推荐。相反,需要创建新选项并手动复制每个属性以及 Converters集合,跳过 converterType 类型的转换.
  • 如果您有 [JsonConverter]应用于自定义值类型或 POCO。似乎没有生成默认序列化的方法。

因为,在问题中,转换器被添加到 Converters列表,以下修改版本正确生成默认序列化:

  1. public sealed class PersonConverter : DefaultConverterFactory<Person>
  2. {
  3. record PersonDTO(string FirstName, string LastName, string Name); // A DTO with both the old and new properties.
  4. protected override Person Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions, JsonConverter<Person> defaultConverter)
  5. {
  6. var dto = JsonSerializer.Deserialize<PersonDTO>(ref reader, modifiedOptions);
  7. var oldNames = dto?.Name?.Split(' ', StringSplitOptions.RemoveEmptyEntries) ?? Enumerable.Empty<string>();
  8. return new Person(dto.FirstName ?? oldNames.FirstOrDefault(), dto.LastName ?? oldNames.LastOrDefault());
  9. }
  10. }
  11. public abstract class DefaultConverterFactory<T> : JsonConverterFactory
  12. {
  13. class DefaultConverter : JsonConverter<T>
  14. {
  15. readonly JsonSerializerOptions modifiedOptions;
  16. readonly DefaultConverterFactory<T> factory;
  17. readonly JsonConverter<T> defaultConverter;
  18. public DefaultConverter(JsonSerializerOptions options, DefaultConverterFactory<T> factory)
  19. {
  20. this.factory = factory;
  21. this.modifiedOptions = options.CopyAndRemoveConverter(factory.GetType());
  22. this.defaultConverter = (JsonConverter<T>)modifiedOptions.GetConverter(typeof(T));
  23. }
  24. public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => factory.Write(writer, value, modifiedOptions, defaultConverter);
  25. public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => factory.Read(ref reader, typeToConvert, modifiedOptions, defaultConverter);
  26. }
  27. protected virtual T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions, JsonConverter<T> defaultConverter)
  28. => defaultConverter.ReadOrSerialize<T>(ref reader, typeToConvert, modifiedOptions);
  29. protected virtual void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions modifiedOptions, JsonConverter<T> defaultConverter)
  30. => defaultConverter.WriteOrSerialize(writer, value, modifiedOptions);
  31. public override bool CanConvert(Type typeToConvert) => typeof(T) == typeToConvert;
  32. public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new DefaultConverter(options, this);
  33. }
  34. public static class JsonSerializerExtensions
  35. {
  36. public static JsonSerializerOptions CopyAndRemoveConverter(this JsonSerializerOptions options, Type converterType)
  37. {
  38. var copy = new JsonSerializerOptions(options);
  39. for (var i = copy.Converters.Count - 1; i >= 0; i--)
  40. if (copy.Converters[i].GetType() == converterType)
  41. copy.Converters.RemoveAt(i);
  42. return copy;
  43. }
  44. public static void WriteOrSerialize<T>(this JsonConverter<T> converter, Utf8JsonWriter writer, T value, JsonSerializerOptions options)
  45. {
  46. if (converter != null)
  47. converter.Write(writer, value, options);
  48. else
  49. JsonSerializer.Serialize(writer, value, options);
  50. }
  51. public static T ReadOrSerialize<T>(this JsonConverter<T> converter, ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
  52. {
  53. if (converter != null)
  54. return converter.Read(ref reader, typeToConvert, options);
  55. else
  56. return (T)JsonSerializer.Deserialize(ref reader, typeToConvert, options);
  57. }
  58. }

笔记:

  • 我使用转换器工厂而不是转换器作为 PersonConverter 的基类因为它让我可以方便地在制造的转换器中缓存复制的选项和默认转换器。
  • 如果您尝试申请 DefaultConverterFactory<T>到自定义值类型或 POCO,例如
    1. [JsonConverter(typeof(PersonConverter))] public record Person(string FirstName, string LastName);

    将发生令人讨厌的堆栈溢出。

演示 fiddle here .

关于c# - 如何在自定义 System.Text.Json JsonConverter 中使用默认序列化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65430420/

 
 

c# - 如何在自定义 System.Text.Json JsonConverter 中使用默认序列化?的更多相关文章

  1. 【译】System.Text.Json 的下一步是什么

    .NET 5.0 最近发布了,并带来了许多新特性和性能改进.System.Text.Json 也不例外.我们改进了性能和可靠性,并使熟悉 Newtonsoft.Json 的人更容易采用它.在这篇文章中 ...

  2. 在.Net Core 3.0中尝试新的System.Text.Json API

    .NET Core 3.0提供了一个名为System.Text.Json的全新命名空间,它支持reader/writer,文档对象模型(DOM)和序列化程序.在此博客文章中,我将介绍它如何工作以及如何 ...

  3. .NET性能系列文章二:Newtonsoft.Json vs. System.Text.Json

    微软终于追上了? 图片来自 Glenn Carstens-Peters Unsplash 欢迎来到.NET性能系列的另一章.这个系列的特点是对.NET世界中许多不同的主题进行研究.基准和比较.正如标题 ...

  4. System.Text.Json 自定义Converter实现时间转换

    Newtonsoft.Json与System.Text.Json区别 在 Newtonsoft.Json中可以使用例如 .AddJsonOptions(options => { options. ...

  5. 使用 System.Text.Json 时,如何处理 Dictionary 中 Key 为自定义类型的问题

    在使用 System.Text.Json 进行 JSON 序列化和反序列化操作时,我们会遇到一个问题:如何处理字典中的 Key 为自定义类型的问题. 背景说明 例如,我们有如下代码:   // 定义一 ...

  6. .NET Core 内置的 System.Text.Json 使用注意

    System.Text.Json 是 .NET Core 3.0 新引入的高性能 json 解析.序列化.反序列化类库,武功高强,但毕竟初入江湖,炉火还没纯青,使用时需要注意,以下是我们在实现使用中遇 ...

  7. .NET Core 3.0 System.Text.Json 和 Newtonsoft.Json 行为不一致问题及解决办法

    行为不一致 .NET Core 3.0 新出了个内置的 JSON 库, 全名叫做尼古拉斯 System.Text.Json - 性能更高占用内存更少这都不是事... 对我来说, 很多或大或小的项目能少 ...

  8. [.Net Core 3.0+/.Net 5] System.Text.Json中时间格式化

    简介 .Net Core 3.0开始全新推出了一个名为System.Text.Json的Json解析库,用于序列化和反序列化Json,此库的设计是为了取代Json.Net(Newtonsoft.Jso ...

  9. 如何使用 System.Text.Json 序列化 DateTimeOffset 为 Unix 时间戳

    在 .NET 中,日期和时间通常使用 DateTime 或 DateTimeOffset 来表示.这两种数据类型都可以表示日期和时间,但它们之间有一些明显的区别.DateTime 是不带时区信息的,而 ...

  10. 从 Newtonsoft.Json 迁移到 System.Text.Json

    一.写在前面 System.Text.Json 是 .NET Core 3 及以上版本内置的 Json 序列化组件,刚推出的时候经常看到踩各种坑的吐槽,现在经过几个版本的迭代优化,提升了易用性,修复了 ...

随机推荐

  1. HarmonyOS SDK,助力开发者打造焕然一新的鸿蒙原生应用

    鸿蒙生态千帆启航仪式于 1 月 18 日正式启动.从 2019 年 HarmonyOS 正式发布到 2020 年"没有人能够熄灭漫天星光",今天,满天星光终汇成璀璨星河,Harmo ...

  2. CentOS下安装OpenSSH 5.8的三种方法【未测试】

    CentOS下安装OpenSSH 5.8的三种方法 Category : 工作学习 / Tags : centos, linux, openssh / Date : 2011.05.19 / 5,29 ...

  3. nginx重新整理——————开篇[一]

    前言 因为整理http协议,顺便把nginx 整理了. 正文 主要作用: 正向代理 反向代理(负载均衡.缓存等) 静态资源 nginx 的优点: 适合高并发,一个进程可以处理很多的请求. 扩展性强,有 ...

  4. Go语言打印九九乘法表,这是整洁代码范例

    Go语言打印九九乘法表,这是整洁代码范例 / Go 语言输出九九乘法表 / 九九乘法表是我们学习编程时的一项基本练习,它看似简单,通过实现输出九九乘法表可以加深对 Go 语言循环结构的理解和运用. 本 ...

  5. flutter 移动应用程序中打开URL

    url_launcher: ^6.2.5   在Flutter中,url_launcher库是用于在移动应用程序中打开URL的常用工具.它允许你通过调用系统的浏览器或其他应用程序来打开指定的URL,比 ...

  6. RocketMQ 消息集成:多类型业务消息——定时消息

    简介: 本篇将继续业务消息集成的场景,从使用场景.应用案例.功能原理以及最佳实践等角度介绍 RocketMQ 的定时消息功能. 作者:凯易.明锻   引言   Apache RocketMQ 诞生至今 ...

  7. 当新零售遇上 Serverless

    ​简介: Serverless 的出现给传统企业数字化转型带了更多机遇. 某零售商超行业的龙头企业,其主要业务涵盖购物中心.大卖场.综合超市.标准超市.精品超市.便利店及无人值守智慧商店等零售业态,涉 ...

  8. [DApp] Moralis 生产阶段的服务安全设置 -锁定数据库

    Moralis 的基础设施数据库是使用的 MongoDB,其非常适合Dev阶段的快速开发. 如果进入生产环境,需要锁定数据库,防止任何用户可利用SDK向Mongo插入多余数据. 另外,Moralis ...

  9. Solution Set - DP

    CF101E Candies and Stones Link&Submission. DP 的状态设计和转移都是显然的,唯一的问题在于需要输出方案,而这题卡空间.会发现如果用 bitset 存 ...

  10. Golang、python中MD5、SHA512、base64编码等

    在GO中处理的话,比较方便. func main() { fmt.Println(md5Str("woGo")) fmt.Println(sha512Str("woGo& ...