如何避免使用 System.Text.JSON 编写"$type"?



在序列化已选择多态反序列化的类时,如何避免公开类型鉴别器?

我正在使用System.Text.Json的JsonDerivedType属性来进行多态反序列化。 所以我需要在属性上指定typeDiscriminator,否则反序列化不会读取 JSON 上的传入$type字段。 但是当我序列化这些相同的类时,我不希望System.Text.Json自动添加$type(公开实现细节等)。

[JsonDerivedType(typeof(Derived1), typeDiscriminator: "Derived1")]
[JsonDerivedType(typeof(Derived2), typeDiscriminator: "Derived2")]
public record BaseType(int Id);
public record Derived1(int Id, string Name) : BaseType(Id);
public record Derived2(int Id, bool IsActive) : BaseType(Id);

序列化时:

var values = new List<BaseType>
{
new Derived1(123, "Foo"),
new Derived2(456, true)
};
JsonSerializer.Serialize(values);

实际输出:

[
{ "$type": "Derived1", "Id": 123, "Name": "Foo" },
{ "$type": "Derived2", "Id": 456, "IsActive": true }
]

如何避免$type被写入?

期望输出:

[
{ "Id": 123, "Name": "Foo" },
{ "Id": 456, "IsActive": true }
]

同样,我知道我可以从属性中排除typeDiscriminator,但是反序列化将不起作用。

链接

  • 反序列化 .NET 7 中添加的类型层次结构

一种解决方案是为序列化和反序列化编写单独的DefaultJsonTypeInfoResolver重写,并删除[JsonDerivedType]属性。

与Newtonsoft类似,System.Text.Json也支持通过序列化程序的选项作为属性的替代方法进行多态反序列化。 在 Newtonsoft 中,您将使用不同的JsonSerializerSettings.TypeNameHandling值来影响是否写入$type。 System.Text.Json 要求您显式选择每种类型加入此行为,但思路是相同的。

示例类:

// Don't add [JsonDerivedType]
public record BaseType(int Id);
public record Derived1(int Id, string Name) : BaseType(Id);
public record Derived2(int Id, bool IsActive) : BaseType(Id);

使用鉴别器反序列化的解析器:

// Example taken from dotnet/runtime#63747 "Configuring Polymorphism via the Contract model"
public class DeserializeResolver : DefaultJsonTypeInfoResolver
{
public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
{
JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options);
if (jsonTypeInfo.Type == typeof(BaseType))
{
jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions()
{
DerivedTypes =
{
// Pass in the type discriminator for this resolver.
new JsonDerivedType(typeof(Derived1), typeDiscriminator: "Derived1"),
new JsonDerivedType(typeof(Derived2), typeDiscriminator: "Derived2"),
}
};
}
return jsonTypeInfo;
}
}

无需鉴别器即可序列化的解析器:

public class SerializeResolver : DefaultJsonTypeInfoResolver
{
...
// Exact same as above resolver except:
DerivedTypes =
{
// Don't pass in the typeDiscriminator parameter.
new JsonDerivedType(typeof(Derived1)),
new JsonDerivedType(typeof(Derived2)),
}
...
}

然后,使用不同的JsonSerializerOptions序列化和反序列化实例:

initial

[
{ "$type": "Derived1", "Name": "Foo", "Id": 123 },
{ "$type": "Derived2", "IsActive": true, "Id": 456 }
]
string initial;
var deserializeOptions = new JsonSerializerOptions
{
TypeInfoResolver = new DeserializeResolver(),
};
var serializeOptions = new JsonSerializerOptions
{
TypeInfoResolver = new SerializeResolver(),
};
var values = JsonSerializer.Deserialize<List<BaseType>>(initial, deserializeOptions);
// At this point, the types have been successfully deserialized via their $type.
Assert.NotNull(values);
Assert.IsAssignableFrom<Derived1>(values[0]);
Assert.IsAssignableFrom<Derived2>(values[1]);
string result = JsonSerializer.Serialize(values, serializeOptions);

result

[
{ "Name": "Foo", "Id": 123 },
{ "IsActive": true, "Id": 456 }
]

我看到以下选项:

  1. 不要使用多态列表,例如,有一个Derived1列表和另一个Derived2列表
  2. $type更改为您喜欢的内容[JsonPolymorphic(CustomTypeDiscriminatorPropertyName = "Kind")]
  3. 使用其他名称:[JsonDerivedType(typeof(Derived1), typeDiscriminator: "A")]或数字[JsonDerivedType(typeof(Derived1), typeDiscriminator: 1)]
  4. 不要使用JsonDerivedType并编写自己的反序列化器

来源:#63747:开发人员可以使用 System.Text.Json 安全地序列化类型层次结构

相关内容

  • 没有找到相关文章

最新更新