Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/google-maps/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 是否有一种简单的方法可以手动序列化/反序列化System.Text.Json中自定义转换器中的子对象?_C#_Json_System.text.json - Fatal编程技术网

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();        
    }
}