反序列化一个 Json 字段有两种类型,一种类型是递归的
Deserialize a Json with a field being of 2 types and one type is recursive
有一个打字稿部分,我从中得到 json 结果,需要在 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 是通过使用 Kendo Ui for Angular 来自 Telerik "CompositeFilterDescriptor"
谢谢。
问题是 C# 没有 'discriminated union' 结构的等价物(至少,我认为它是这样叫的 ;-))
可用于多态性的唯一构造是接口和继承。
在这种情况下,您需要两种类型(FilterDescriptor
和 CompositeFilterDescriptor
),并且您需要多态性,因为 CompositeFilterDescriptor
有一个数组可以包含其中任何一种。在 C# 中,您需要一个接口或基础 class 来表达:
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());
有一个打字稿部分,我从中得到 json 结果,需要在 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 是通过使用 Kendo Ui for Angular 来自 Telerik "CompositeFilterDescriptor"
谢谢。
问题是 C# 没有 'discriminated union' 结构的等价物(至少,我认为它是这样叫的 ;-))
可用于多态性的唯一构造是接口和继承。
在这种情况下,您需要两种类型(FilterDescriptor
和 CompositeFilterDescriptor
),并且您需要多态性,因为 CompositeFilterDescriptor
有一个数组可以包含其中任何一种。在 C# 中,您需要一个接口或基础 class 来表达:
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());