.net - 使用 System.Text.Json.Serialization.JsonConverter 解决 JSON 序列化过程中复杂类型的循环引用
问题描述
有一个复杂类型引用了相同类型的对象(有时是相同的对象):
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public User Reference { get; set; }
}
有一个自定义的 JsonConverter (System.Text.Json.Serialization) 实现来反序列化这个对象,避免一些特殊的属性。
public class UserJsonConverter : JsonConverter<User>
{
public override User Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, User value, JsonSerializerOptions options)
{
writer.WriteStartObject();
// write the name property only and ignore the age
writer.WriteString(nameof(value.Name), value.Name);
writer.WritePropertyName(nameof(value.Reference));
JsonSerializer.Serialize(writer, value.Reference, options);
writer.WriteEndObject();
}
}
但是在对象指向自身的情况下如何配置引用解析尚不清楚。例子:
var user = new User
{
Age = 10,
Name = "username"
};
user.Reference = user;
var options = new JsonSerializerOptions();
options.ReferenceHandler = ReferenceHandler.Preserve;
options.Converters.Add(new UserJsonConverter());
var result = JsonSerializer.Serialize(user, user.GetType(), options);
发生异常:
System.Text.Json.JsonException. A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 64. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles
我们使用 System.Text.Json,版本=5.0.0.0
默认对象转换器的所有基于 '$ref' 和 '$id' 的方法都是内部的,无法使用。我看到的唯一方法是在序列化之前在某些 DTO 中简化 User 对象,并在完全没有此自定义转换器的情况下使用它。
但也许有人知道在自定义 JsonConverter 中是否有解决这些引用的正确方法?
解决方案
基于自定义引用处理程序使用的解决方案
UserJsonConverter 更改:
public override void Write(Utf8JsonWriter writer, User value, JsonSerializerOptions options)
{
// use reference handler manually
var v_resolver = options.ReferenceHandler?.CreateResolver();
if (v_resolver != null)
{
var v_refID = v_resolver.GetReference(value, out bool alreadyExists);
if (alreadyExists)
{
writer.WriteStartObject();
writer.WriteString(JsonEncodedText.Encode("$ref", encoder: null), v_refID);
writer.WriteEndObject();
return;
}
else
{
writer.WriteStartObject();
writer.WriteString(JsonEncodedText.Encode("$id", encoder: null), v_refID);
}
}
//writer.WriteStartObject();
...
}
使用变化:
...
var options = new JsonSerializerOptions();
options.ReferenceHandler = new CutomReferenceHandler();
...
自定义参考处理程序实现可在此处获得:https ://github.com/dotnet/docs/issues/21777#issuecomment-736751404 或文档
它可以工作,但我有点害怕以这种方式使用它,因为将来可能会更改内部引用解析器逻辑($id,$refs)。
推荐阅读
- java - 如何在布局底部更改我的相机位置
- sas - 如何获得行中有 2 个变量,列中有 1 个变量的频率表?
- json - 如何在 DjangoRestFramework UI 中获得格式更好的 JSON 响应
- php - 如何在 jQuery 中获取 Ajax 响应的值
- google-analytics - 谷歌标签管理器 - 合并来自“旧”分析的数据
- php - 两个数组合二为一 foreach Laravel
- julia - 这个 Julia 直方图中缺少的逗号或 () 在哪里?
- javascript - querySelectorAll vs NodeIterator vs TreeWalker - 最快的纯 JS 平面 DOM 迭代器
- google-my-business-api - 如何使用 GMB API 制作带有媒体的 localPost?
- php - 使用查询生成器在分组查询中获取 COUNT(),在 SELECT 中使用分组列