c# - 如何在自定义 System.Text.Json JsonConverter 中使用默认序列化?
我正在写一个 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
Converterscollection.[JsonConverter]applied to a custom value type or POCO.
每个案例都需要单独处理。
- 如果您有
[JsonConverter]应用于属性。然后只需调用JsonSerializer.Serialize(writer, person, options);将生成默认序列化。 - 如果您将 A 转换器添加到
Converters集合。然后在Write()里面(或Read()) 方法,可以复制传入的options使用JsonSerializerOptionscopy 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 中使用默认序列化?的更多相关文章
- 【译】System.Text.Json 的下一步是什么
.NET 5.0 最近发布了,并带来了许多新特性和性能改进.System.Text.Json 也不例外.我们改进了性能和可靠性,并使熟悉 Newtonsoft.Json 的人更容易采用它.在这篇文章中 ...
- 在.Net Core 3.0中尝试新的System.Text.Json API
.NET Core 3.0提供了一个名为System.Text.Json的全新命名空间,它支持reader/writer,文档对象模型(DOM)和序列化程序.在此博客文章中,我将介绍它如何工作以及如何 ...
- .NET性能系列文章二:Newtonsoft.Json vs. System.Text.Json
微软终于追上了? 图片来自 Glenn Carstens-Peters Unsplash 欢迎来到.NET性能系列的另一章.这个系列的特点是对.NET世界中许多不同的主题进行研究.基准和比较.正如标题 ...
- System.Text.Json 自定义Converter实现时间转换
Newtonsoft.Json与System.Text.Json区别 在 Newtonsoft.Json中可以使用例如 .AddJsonOptions(options => { options. ...
- 使用 System.Text.Json 时,如何处理 Dictionary 中 Key 为自定义类型的问题
在使用 System.Text.Json 进行 JSON 序列化和反序列化操作时,我们会遇到一个问题:如何处理字典中的 Key 为自定义类型的问题. 背景说明 例如,我们有如下代码: // 定义一 ...
- .NET Core 内置的 System.Text.Json 使用注意
System.Text.Json 是 .NET Core 3.0 新引入的高性能 json 解析.序列化.反序列化类库,武功高强,但毕竟初入江湖,炉火还没纯青,使用时需要注意,以下是我们在实现使用中遇 ...
- .NET Core 3.0 System.Text.Json 和 Newtonsoft.Json 行为不一致问题及解决办法
行为不一致 .NET Core 3.0 新出了个内置的 JSON 库, 全名叫做尼古拉斯 System.Text.Json - 性能更高占用内存更少这都不是事... 对我来说, 很多或大或小的项目能少 ...
- [.Net Core 3.0+/.Net 5] System.Text.Json中时间格式化
简介 .Net Core 3.0开始全新推出了一个名为System.Text.Json的Json解析库,用于序列化和反序列化Json,此库的设计是为了取代Json.Net(Newtonsoft.Jso ...
- 如何使用 System.Text.Json 序列化 DateTimeOffset 为 Unix 时间戳
在 .NET 中,日期和时间通常使用 DateTime 或 DateTimeOffset 来表示.这两种数据类型都可以表示日期和时间,但它们之间有一些明显的区别.DateTime 是不带时区信息的,而 ...
- 从 Newtonsoft.Json 迁移到 System.Text.Json
一.写在前面 System.Text.Json 是 .NET Core 3 及以上版本内置的 Json 序列化组件,刚推出的时候经常看到踩各种坑的吐槽,现在经过几个版本的迭代优化,提升了易用性,修复了 ...
随机推荐
- Mybatis终极案例之注解开发
Mybatis终极案例之注解开发 一.注解开发实现CURD操作 1.环境搭建 参考入门案例,环境搭建主要分为如下几步: 1.配置pom.xml <packaging>jar</pac ...
- linux 性能自我学习 ———— cpu 高怎么办 [三]
前言 linux 性能分析自我学习. 正文 一般我们说cpu,一般是什么高呢? 一般是指cpu 使用率高. 那么什么是cpu 使用率呢? cpu 使用率 = 1- 空闲时间/总cpu 时间 平均cpu ...
- Thread.Suspend和Abort 的区别
理解: 在C#中,Thread.Suspend是一个方法,用于暂停当前线程的执行.它会导致线程停止执行并进入挂起状态,直到调用Thread.Resume方法才能继续执行. 然而,Thread.Susp ...
- 轻松复现一张AI图片
轻松复现一张AI图片 现在有一个非常漂亮的AI图片,你是不是想知道他是怎么生成的? 今天我会交给大家三种方法,学会了,什么图都可以手到擒来了. 需要的软件 在本教程中,我们将使用AUTOMATIC11 ...
- python:在cmd中输入pip install pandas 显示‘pip’不是内部或外部命令,也不是可运行的程序 或批处理文件解决办法
1.首先找到自己文件夹中python的安装位置,一定要在Scripts文件夹下,可以看到pip文件,复制这时候的路径 2.使用快捷键 "win +R"打开cmd窗口,首先进入自己 ...
- ESXI 6.5 零基础从安装到批量生成/管理虚拟机简易教程
制造U盘安装盘 1 先提前下载好,ESXI 6.5 ISO文件. 2 下载制作U盘安装工具,RUFUS. Rufus非常小巧的绿色EXE文件,默认配置选中ISO文件就可以,点击开始,就自动制作,非常方 ...
- 说说关系型数据库与Serverless
简介: 看到如今Serverless在云计算行业喷薄欲出的态势,像极了<星星之火,可以燎原>中的描述:虽然不能预测未来的发展和变化,但对于云计算来说这是个相对确定的方向.本文从产业界和学术 ...
- 一文读懂容器存储接口 CSI
简介: 在<一文读懂 K8s 持久化存储流程>一文我们重点介绍了 K8s 内部的存储流程,以及 PV.PVC.StorageClass.Kubelet 等之间的调用关系.接下来本文将将重点 ...
- [Go] CORS 支持多个 origin 访问的思路 (Access-Control-Allow-Origin 部分)
以下为局部伪代码,仅供参考: var allowOrigin string allowOrigins := config.AppConf.Get("middleware.cors.allow ...
- 【Flink入门修炼】2-3 Flink Checkpoint 原理机制
如果让你来做一个有状态流式应用的故障恢复,你会如何来做呢? 单机和多机会遇到什么不同的问题? Flink Checkpoint 是做什么用的?原理是什么? 一.什么是 Checkpoint? Chec ...