C# 是否有一种简单的方法可以手动序列化/反序列化System.Text.Json中自定义转换器中的子对象?
注意:我使用的是微软新推出的C# 是否有一种简单的方法可以手动序列化/反序列化System.Text.Json中自定义转换器中的子对象?,c#,json,system.text.json,C#,Json,System.text.json,注意:我使用的是微软新推出的System.Text.Json,而不是Json.NET,因此请确保答案相应地解决了这个问题 考虑以下简单的POCO: 接口车辆{} 汽车类别:汽车{ 字符串make{get;set;} int numberOfDoors{get;set;} } 自行车类别:汽车{ int frontGears{get;set;} int backGears{get;set;} } 汽车可以用JSON表示,如下所示 { “制造”:“智能”, “门数”:2 } 自行车可以这样表示
System.Text.Json
,而不是Json.NET
,因此请确保答案相应地解决了这个问题
考虑以下简单的POCO:
接口车辆{}
汽车类别:汽车{
字符串make{get;set;}
int numberOfDoors{get;set;}
}
自行车类别:汽车{
int frontGears{get;set;}
int backGears{get;set;}
}
汽车可以用JSON表示,如下所示
{
“制造”:“智能”,
“门数”:2
}
自行车可以这样表示
{
"前档":三,,
“后盾”:6
}
非常直截了当。现在考虑这个JSON。< /P>
[
{
“汽车”:{
“制造”:“智能”,
“门数”:2
}
},
{
“汽车”:{
“制造”:“雷克萨斯”,
“门数”:4
}
},
{
“自行车”:{
"前档":三,,
“后盾”:6
}
}
]
这是一个对象数组,其中属性名称是了解相应嵌套对象所指类型的关键
虽然我知道如何编写自定义转换器,使用UTF8JsonReader
读取属性名称(例如“Car”和“Bicycle”,并可以相应地编写switch语句,但我不知道的是如何返回到默认的Car
和Bicycle
转换器(即标准JSON转换器)因为我在读卡器上看不到任何在特定类型对象中读取的方法
那么,如何手动反序列化这样的嵌套对象呢?我找到了答案。您只需将读写器向下传递到JsonSerializer的另一个实例,它就会像处理本机对象一样处理它 下面是一个完整的示例,您可以将其粘贴到RoslynPad中,然后运行它 下面是实现
using System;
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
public class HeterogenousListConverter<TItem, TList> : JsonConverter<TList>
where TItem : notnull
where TList : IList<TItem>, new() {
public HeterogenousListConverter(params (string key, Type type)[] mappings){
foreach(var (key, type) in mappings)
KeyTypeLookup.Add(key, type);
}
public ReversibleLookup<string, Type> KeyTypeLookup = new ReversibleLookup<string, Type>();
public override bool CanConvert(Type typeToConvert)
=> typeof(TList).IsAssignableFrom(typeToConvert);
public override TList Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options){
// Helper function for validating where you are in the JSON
void validateToken(Utf8JsonReader reader, JsonTokenType tokenType){
if(reader.TokenType != tokenType)
throw new JsonException($"Invalid token: Was expecting a '{tokenType}' token but received a '{reader.TokenType}' token");
}
validateToken(reader, JsonTokenType.StartArray);
var results = new TList();
reader.Read(); // Advance to the first object after the StartArray token. This should be either a StartObject token, or the EndArray token. Anything else is invalid.
while(reader.TokenType == JsonTokenType.StartObject){ // Start of 'wrapper' object
reader.Read(); // Move to property name
validateToken(reader, JsonTokenType.PropertyName);
var typeKey = reader.GetString();
reader.Read(); // Move to start of object (stored in this property)
validateToken(reader, JsonTokenType.StartObject); // Start of vehicle
if(KeyTypeLookup.TryGetValue(typeKey, out var concreteItemType)){
var item = (TItem)JsonSerializer.Deserialize(ref reader, concreteItemType, options);
results.Add(item);
}
else{
throw new JsonException($"Unknown type key '{typeKey}' found");
}
reader.Read(); // Move past end of item object
reader.Read(); // Move past end of 'wrapper' object
}
validateToken(reader, JsonTokenType.EndArray);
return results;
}
public override void Write(Utf8JsonWriter writer, TList items, JsonSerializerOptions options){
writer.WriteStartArray();
foreach (var item in items){
var itemType = item.GetType();
writer.WriteStartObject();
if(KeyTypeLookup.ReverseLookup.TryGetValue(itemType, out var typeKey)){
writer.WritePropertyName(typeKey);
JsonSerializer.Serialize(writer, item, itemType, options);
}
else{
throw new JsonException($"Unknown type '{itemType.FullName}' found");
}
writer.WriteEndObject();
}
writer.WriteEndArray();
}
}
使用系统;
使用System.Collections.ObjectModel;
使用System.Text.Json;
使用System.Text.Json.Serialization;
公共类HeterogenousListConverter:JsonConverter
其中TItem:notnull
其中TList:IList,new(){
公共HeterogenousListConverter(参数(字符串键,类型)[]映射){
foreach(映射中的var(键,类型)
添加(键,类型);
}
public ReversibleLookup KeyTypeLookup=新建ReversibleLookup();
公共覆盖布尔CanConvert(类型为typeToConvert)
=>typeof(TList)。可从(typeToConvert)中指定;
公共重写TList Read(参考Utf8JsonReader reader,键入typeToConvert,JsonSerializerOptions选项){
//帮助函数,用于验证您在JSON中的位置
void validateToken(Utf8JsonReader阅读器,JsonTokenType令牌类型){
if(reader.TokenType!=TokenType)
抛出新的JsonException($“无效令牌:应为“{tokenType}”令牌,但收到“{reader.tokenType}”令牌”);
}
validateToken(阅读器,JsonTokenType.StartArray);
var results=new TList();
reader.Read();//前进到StartArray标记后的第一个对象。这应该是StartObject标记或EndArray标记。其他任何内容都无效。
而(reader.TokenType==JsonTokenType.StartObject){//“包装器”对象的开始
reader.Read();//移动到属性名
validateToken(读取器,JsonTokenType.PropertyName);
var typeKey=reader.GetString();
reader.Read();//移动到对象的开头(存储在此属性中)
validateToken(读取器,JsonTokenType.StartObject);//启动车辆
if(KeyTypeLookup.TryGetValue(typeKey,out-var-concreteItemType)){
var item=(TItem)JsonSerializer.Deserialize(ref reader,concreteItemType,options);
结果:增加(项目);
}
否则{
抛出新的JsonException($“找到未知类型键{typeKey}”);
}
reader.Read();//移过项对象的末尾
reader.Read();//移动到“包装器”对象的末尾
}
validateToken(读取器,JsonTokenType.EndArray);
返回结果;
}
公共重写无效写入(Utf8JsonWriter编写器、TList项、JsonSerializerOptions选项){
writer.writestarray();
foreach(项目中的var项目){
var itemType=item.GetType();
writer.WriteStartObject();
if(KeyTypeLookup.ReverseLookup.TryGetValue(itemType,out-var-typeKey)){
writer.WritePropertyName(typeKey);
序列化(writer、item、itemType、options);
}
否则{
抛出新的JsonException($“找到未知类型{itemType.FullName}”);
}
writer.WriteEndObject();
}
writer.WriteEndArray();
}
}
这是演示代码
#nullable disable
public interface IVehicle { }
public class Car : IVehicle {
public string make { get; set; } = null;
public int numberOfDoors { get; set; } = 0;
public override string ToString()
=> $"{make} with {numberOfDoors} doors";
}
public class Bicycle : IVehicle{
public int frontGears { get; set; } = 0;
public int backGears { get; set; } = 0;
public override string ToString()
=> $"{nameof(Bicycle)} with {frontGears * backGears} gears";
}
string json = @"[
{
""Car"": {
""make"": ""Smart"",
""numberOfDoors"": 2
}
},
{
""Car"": {
""make"": ""Lexus"",
""numberOfDoors"": 4
}
},
{
""Bicycle"": {
""frontGears"": 3,
""backGears"": 6
}
}
]";
var converter = new HeterogenousListConverter<IVehicle, ObservableCollection<IVehicle>>(
(nameof(Car), typeof(Car)),
(nameof(Bicycle), typeof(Bicycle))
);
var options = new JsonSerializerOptions();
options.Converters.Add(converter);
var vehicles = JsonSerializer.Deserialize<ObservableCollection<IVehicle>>(json, options);
Console.Write($"{vehicles.Count} Vehicles: {String.Join(", ", vehicles.Select(v => v.ToString())) }");
var json2 = JsonSerializer.Serialize(vehicles, options);
Console.WriteLine(json2);
Console.WriteLine($"Completed at {DateTime.Now}");
#可空禁用
公共接口IVehile{}
公车:IVehicle{
公共字符串make{get;set;}=null;
公共int numberOfDoors{get;set;}=0;
公共重写字符串ToString()
=>$“{make}和{numberOfDoors}门”;
}
公共级自行车:IVehicle{
public int frontGears{get;set;}=0;
公共int backGears{get;set;}=0;
公共重写字符串ToString()
=>$“{nameof(自行车)}带{frontGears*backGears}档位”;
}
字符串json=@”[
{
“汽车”:{
“make”“:“Smart”“,
“numberOfDoors”:2
}
},
{
“汽车”:{
“制造”:“雷克萨斯”,
“numberOfDoors”:4
}
},
{
“自行车”:{
“frontGears”:3,
“后置装置”:6
}
}
]";
var转换器=新的异源ListConverter(
(车辆名称,车辆类型),
(自行车名称,自行车类型)
);
var options=新的JsonSerializerOptions();
选项。转换器。添加(转换器);
var vehicles=JsonSerializer.反序列化
using System.Collections.ObjectModel;
using System.Diagnostics;
public class ReversibleLookup<T1, T2> : ReadOnlyDictionary<T1, T2>
where T1 : notnull
where T2 : notnull {
public ReversibleLookup(params (T1, T2)[] mappings)
: base(new Dictionary<T1, T2>()){
ReverseLookup = new ReadOnlyDictionary<T2, T1>(reverseLookup);
foreach(var mapping in mappings)
Add(mapping.Item1, mapping.Item2);
}
private readonly Dictionary<T2, T1> reverseLookup = new Dictionary<T2, T1>();
public ReadOnlyDictionary<T2, T1> ReverseLookup { get; }
[DebuggerHidden]
public void Add(T1 value1, T2 value2) {
if(ContainsKey(value1))
throw new InvalidOperationException($"{nameof(value1)} is not unique");
if(ReverseLookup.ContainsKey(value2))
throw new InvalidOperationException($"{nameof(value2)} is not unique");
Dictionary.Add(value1, value2);
reverseLookup.Add(value2, value1);
}
public void Clear(){
Dictionary.Clear();
reverseLookup.Clear();
}
}