我正在写一个 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. k8s之基于metallb实现LoadBalancer型Service

    一.实验说明 1.介绍 MetalLB 是裸机 Kubernetes 集群的负载均衡器实现,使用标准路由协议,主要用于暴露 K8s 集群的服务到集群外部访问,MetalLB 可以让我们在 K8s 集群 ...

  2. k8s之构建Mysql和Wordpress集群

    一.实验目的 基于Kubernetes集群实现多负载的WordPress应用.将WordPress数据存储在后端Mysql,Mysql实现主从复制读写分离功能. 工作负载 服务 持久卷 Mysql S ...

  3. Telnet qsnctfwp

    Windows 安装 Telnet 在控制面板的程序和功能中选择打开或关闭Windows功能 启用 Telnet 客户端并单击确认退出 启动终端,输入命令 telnet 打开 Telnet 客户端 在 ...

  4. maven 设置阿里镜像[二]

    前言 因为我们在国内,网速很慢,所以最好设置一下阿里镜像. 1.在maven中的conf下的setting 修改为: <mirror> <id>alimaven</id& ...

  5. CentOS 7.9编译安装Python-3.10.13

    目录 查看CentOS版本.系统默认gcc版本.Python版本和pip版本 部署Python-3.10.13 测试 将yum中的Python版本修改为系统原来的2.7.5版本 查看CentOS版本. ...

  6. 力扣1076(MySQL)-员工项目Ⅱ(简单)

    题目: 编写一个SQL查询,报告所有雇员最多的项目. 查询结果格式如下所示:  解题思路: 方法一:将两个表联结,以project_id进行分组,统计员工数降序排序,然后筛选出第一条数据. 1 sel ...

  7. 力扣319(java)-灯泡开关(中等)

    题目: 初始时有 n 个灯泡处于关闭状态.第一轮,你将会打开所有灯泡.接下来的第二轮,你将会每两个灯泡关闭第二个. 第三轮,你每三个灯泡就切换第三个灯泡的开关(即,打开变关闭,关闭变打开).第 i 轮 ...

  8. 深度解析开源推荐算法框架EasyRec的核心概念和优势

    ​简介:如何通过机器学习PAI实现快速构建推荐模型 作者:程孟力 - 机器学习PAI团队 随着移动app的普及,个性化推荐和广告成为很多app不可或缺的一部分.他们在改善用户体验和提升app的收益方面 ...

  9. MaxCompute Spark 资源使用优化祥解

    简介: 本文主要讲解MaxCompute Spark资源调优,目的在于在保证Spark任务正常运行的前提下,指导用户更好地对Spark作业资源使用进行优化,极大化利用资源,降低成本. 本文作者:吴数傑 ...

  10. Java中的读写锁ReentrantReadWriteLock详解,存在一个小缺陷

    写在开头 最近是和java.util.concurrent.locks包下的同步类干上了,素有 并发根基 之称的concurrent包中全是精品,今天我们继续哈,今天学习的主题要由一个大厂常问的Jav ...