反序列化具有 2 种类型字段且一种类型是递归的 Json

Posted

技术标签:

【中文标题】反序列化具有 2 种类型字段且一种类型是递归的 Json【英文标题】:Deserialize a Json with a field being of 2 types and one type is recursive 【发布时间】:2021-01-21 10:05:31 【问题描述】:

有一个我从中得到 json 结果的 typescript 部分,需要在 c# 中解析结果以获取使用它的对象,但是因为我们在这里:

    多类型字段 递归

越来越难以理解反序列化器的外观, 我知道我需要将 JsonConverter 用于多类型字段,但是如何处理沿多类型字段的递归?

这是制作 json 的打字稿代码:

export interface FilterDescriptor 

  field ? : string | Function;

  operator: string | Function;

  value ? : any;

  ignoreCase ? : boolean;


export interface CompositeFilterDescriptor 
  logic: 'or' | 'and';

  filters: Array < FilterDescriptor | CompositeFilterDescriptor > ;


export declare
const isCompositeFilterDescriptor: (source: FilterDescriptor | CompositeFilterDescriptor) => source is CompositeFilterDescriptor;

json 的一个例子: 递归


"logic": "and",
"filters": [
  
    "field": "type.group",
    "logic": "and",
    "filters": [
      
        "field": "type.group",
        "operator": "neq",
        "value": 2
      ,
      
        "field": "type.group",
        "operator": "neq",
        "value": 5
      
    ]
  
]

没有递归:


"logic": "and",
"filters": [
  
    "field": "type.group",
    "operator": "eq",
    "value": 2
  
]

这个 json 是使用来自 Telerik "CompositeFilterDescriptor" 的 Kendo Ui for Angular 生成的

谢谢。

【问题讨论】:

值字段是number吗? 它也可以是一个字符串,甚至是一个像“2,4,5”这样的数组 嗯.. 它更复杂。 sidequestion:这是您的递归示例,最大递归数还是更多? 理论上可以有好几层,我的只有2层。 【参考方案1】:

问题在于 C# 没有“可区分联合”构造的等价物(至少,我认为这就是所谓的 ;-))

可用于多态性的唯一构造是接口和继承。

在这种情况下,您需要两种类型(FilterDescriptorCompositeFilterDescriptor),并且需要多态性,因为CompositeFilterDescriptor 有一个可以包含其中任何一种的数组。在 C# 中,您需要一个接口或基类来表达:

interface IFilterDescriptor  

class FilterDescriptor : IFilterDescriptor

    // ... (fields of FilterDescriptor)


class CompositeFilterDescriptor : IFilterDescriptor

    public string @logic  get; set; 
    public IFilterDescriptor[] filters  get; set; 

此外,您不能使用 Json.NET 默认反序列化,因为多态反序列化需要类型信息。但是,您可以创建一个自定义转换器,您可以在其中检查某些字段的存在以确定如何反序列化对象:

public class FilterDescriptorConverter : JsonConverter

    public override bool CanConvert(Type objectType) => typeof(IFilterDescriptor).IsAssignableFrom(objectType);

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    
        JObject o = JObject.Load(reader);

        // if the 'logic' field is present, it's a CompositeFilter
        var item = o["logic"] != null 
            ? (IFilterDescriptor)new CompositeFilterDescriptor() 
            : (IFilterDescriptor)new FilterDescriptor();
        serializer.Populate(o.CreateReader(), item);
        return item;
    

    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();

你可以这样使用它:

var result = JsonConvert.DeserializeObject<IFilterDescriptor>(json, 
          new FilterDescriptorConverter());

【讨论】:

以上是关于反序列化具有 2 种类型字段且一种类型是递归的 Json的主要内容,如果未能解决你的问题,请参考以下文章

使用 Moshi 反序列化可以是两种数据类型之一的字段

如何将具有固定模式的值数组反序列化为强类型数据类?

反序列化具有在 AssemblyResolve 上加载的其他程序集中声明的类型字段的对象时出错

j.net 实现json的序列化与反序列化

干货Java反序列化漏洞总结

将 JSON 反序列化为具有通用字段的 Serializable 类 - 错误:不允许类型参数中的星形投影