有一个复杂类型,它引用了同一类型的对象(有时是同一个对象):
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, Version = 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)。