我正在编写一个custom System.Text.Json.JsonConverter<T>
来将旧数据模型升级到新版本。我已经覆盖了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"}
,即串行化期间的CAMEL 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
属性等效的属性。
演示小提琴here。
1条答案
按热度按时间cbjzeqam1#
如文档中所述,转换器的选择优先级如下:
[JsonConverter]
应用于属性。Converters
集合的转换器。[JsonConverter]
应用于自定义值类型或POCO。此外,还有另一种情况:
JsonConverterFactory
返回的JsonConverter<T>
。每种情况都需要单独处理。
1.如果您将
[JsonConverter]
应用于属性.,则只需调用JsonSerializer.Serialize(writer, person, options);
即可生成默认序列化。1.如果您有 A converter added to the
Converters
collection.,那么在Write()
(或Read()
)方法中,您可以使用JsonSerializerOptions
复制构造函数复制传入的options
,从副本的Converters
列表中删除转换器,并将修改后的副本传递到JsonSerializer.Serialize<T>(Utf8JsonWriter, T, JsonSerializerOptions);
这在.NET Core 3.x中无法轻松完成,因为该版本中不存在复制构造函数。临时修改传入选项的
Converters
集合以删除转换器不是线程安全的,因此不建议这样做。相反,需要创建新选项并手动复制每个属性以及Converters
集合,跳过converterType
类型的转换。请注意,这将导致递归类型(如树)的序列化出现问题,因为相同类型的嵌套对象最初不会使用转换器进行序列化。
1.如果您将
[JsonConverter]
应用于自定义值类型或POCO.,则似乎没有生成默认序列化的方法。1.转换器列表中的
JsonConverterFactory
返回JsonConverter<T>
的情况在此答案中没有解决,因为有必要禁用工厂,而不仅仅是转换器。在这种情况下,不清楚是完全禁用工厂,还是仅针对特定的具体类型T
禁用工厂。由于在问题中,转换器被添加到
Converters
列表中,因此以下修改版本正确地生成了默认序列化:注意事项:
PersonConverter
的基类,因为它允许我方便地将复制的选项缓存在制造的转换器中。DefaultConverterFactory<T>
应用于自定义值类型或POCO,例如将发生严重的堆栈溢出。
演示小提琴here。