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

public record Person(string Name);

我已经升级到

public record Person(string FirstName, string LastName);

我写了一个转换器如下:

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

和往返

var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Converters = { new PersonConverter() },
};
var person = JsonSerializer.Deserialize<Person>(json, options);
var json2 = JsonSerializer.Serialize(person, options);

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

    public override void Write(Utf8JsonWriter writer, Person person, JsonSerializerOptions options)
=> // What do I do here? I want to preserve other options such as options.PropertyNamingPolicy, which are lost by the following call
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列表,以下修改版本正确生成默认序列化:

public sealed class PersonConverter : DefaultConverterFactory<Person>
{
record PersonDTO(string FirstName, string LastName, string Name); // A DTO with both the old and new properties. protected override Person Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions, JsonConverter<Person> defaultConverter)
{
var dto = JsonSerializer.Deserialize<PersonDTO>(ref reader, modifiedOptions);
var oldNames = dto?.Name?.Split(' ', StringSplitOptions.RemoveEmptyEntries) ?? Enumerable.Empty<string>();
return new Person(dto.FirstName ?? oldNames.FirstOrDefault(), dto.LastName ?? oldNames.LastOrDefault());
}
} public abstract class DefaultConverterFactory<T> : JsonConverterFactory
{
class DefaultConverter : JsonConverter<T>
{
readonly JsonSerializerOptions modifiedOptions;
readonly DefaultConverterFactory<T> factory;
readonly JsonConverter<T> defaultConverter; public DefaultConverter(JsonSerializerOptions options, DefaultConverterFactory<T> factory)
{
this.factory = factory;
this.modifiedOptions = options.CopyAndRemoveConverter(factory.GetType());
this.defaultConverter = (JsonConverter<T>)modifiedOptions.GetConverter(typeof(T));
} public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => factory.Write(writer, value, modifiedOptions, defaultConverter); public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => factory.Read(ref reader, typeToConvert, modifiedOptions, defaultConverter);
} protected virtual T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions, JsonConverter<T> defaultConverter)
=> defaultConverter.ReadOrSerialize<T>(ref reader, typeToConvert, modifiedOptions); protected virtual void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions modifiedOptions, JsonConverter<T> defaultConverter)
=> defaultConverter.WriteOrSerialize(writer, value, modifiedOptions); public override bool CanConvert(Type typeToConvert) => typeof(T) == typeToConvert; public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new DefaultConverter(options, this);
} public static class JsonSerializerExtensions
{
public static JsonSerializerOptions CopyAndRemoveConverter(this JsonSerializerOptions options, Type converterType)
{
var copy = new JsonSerializerOptions(options);
for (var i = copy.Converters.Count - 1; i >= 0; i--)
if (copy.Converters[i].GetType() == converterType)
copy.Converters.RemoveAt(i);
return copy;
} public static void WriteOrSerialize<T>(this JsonConverter<T> converter, Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
if (converter != null)
converter.Write(writer, value, options);
else
JsonSerializer.Serialize(writer, value, options);
} public static T ReadOrSerialize<T>(this JsonConverter<T> converter, ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (converter != null)
return converter.Read(ref reader, typeToConvert, options);
else
return (T)JsonSerializer.Deserialize(ref reader, typeToConvert, options);
}
}

笔记:

  • 我使用转换器工厂而不是转换器作为 PersonConverter 的基类因为它让我可以方便地在制造的转换器中缓存复制的选项和默认转换器。
  • 如果您尝试申请 DefaultConverterFactory<T>到自定义值类型或 POCO,例如
    [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宠物健康系统的开发分享

    原文:https://mp.weixin.qq.com/s/upcS6PcMS7UBR5jgoP7eow,点击链接查看更多技术内容. 本期我们给大家带来的是家庭宠物健康监测系统开发者杨光的分享,希望能 ...

  2. linux 性能自我学习 ———— cpu 高怎么办 [三]

    前言 linux 性能分析自我学习. 正文 一般我们说cpu,一般是什么高呢? 一般是指cpu 使用率高. 那么什么是cpu 使用率呢? cpu 使用率 = 1- 空闲时间/总cpu 时间 平均cpu ...

  3. mmcls 多标签模型部署在torch serve

    GitHub仓库:gy-7/mmcls_multi_label_torchserve (github.com) 各个文件说明: cls_requests_demo:分类模型请求api服务的demo d ...

  4. python 远程windows系统执行cmd命令

    如果你的服务器是windows系统,不想一台一台mstsc远程到桌面上去操作,python是有模块可以远程处理的:winrm pip install pywinrm 安装模块即可 windows系统服 ...

  5. 【笔记】Oracle列转行unpivot&行转列 PIVOT

    unpivot 说明:将表中多个列缩减为一个聚合列(多列转多行) 语法:unpivot(新列名 for 聚合列名 in (对应的列名1-列名n )) 写到了一个力扣的题,发现这个unpivot函数还没 ...

  6. 当Java遇上机密计算,又一段奇幻之旅开始了!

    ​简介: 汪少军:如何为Java业务提供机密计算保护? ​ 写在前面 在信息世界里,数据存在三种状态: 存储态.传输态和计算态.存储在数据库或磁盘中的数据属于存储状态,在网络中传输的数据属于传输状态, ...

  7. Dataphin功能:集成——如何将业务系统的数据抽取汇聚到数据中台

    ​简介: 数据集成是简单高效的数据同步平台,致力于提供具有强大的数据预处理能力.丰富的异构数据源之间数据高速稳定的同步能力,为数据中台的建设打好坚实的数据基座. 数据中台是当下大数据领域最前沿的数据建 ...

  8. [Caddy2] Caddyfile 静态文件托管 file_server 的 hide 用法

      file_server 语法: file_server [<matcher>] [browse] { root <path> hide <files...> i ...

  9. WPF 已知问题 传入错误数据给到 WriteableBitmap 可能导致渲染线程锁住

    本文记录一个 WPF 已知问题,此问题已经被我修复.传入错误的数据给到 WriteableBitmap 对象,比如调用 WritePixels 时传入错误的 stride 数值,将可能导致渲染线程进入 ...

  10. WPF 在 .NET Core 3.1.19 版本 触摸笔迹偏移问题

    在更新到 .NET 6 发布之前的,在 2021.11.02 的 .NET Core 版本,都会存在此问题.在 WPF 应用里面,如果在高 DPI 下,进行触摸书写,此时的笔迹将会偏移.核心原因是在这 ...