使用泛型时 System.Text.Json 的自定义转换器
Custom converter for System.Text.Json when working with generics
我有一个包含通用 (object
) 的 class,可以用 string
、int
、bool
或相同 class 的另一个实例。以前,在使用 Newtonsoft.Json 时,我有一个自定义转换器,它会根据 JToken.Type
是否为 JTokenType.Object
来确定如何反序列化(检测该值可能是相同 [= 的另一个实例) 56=]).
使用 System.Text.Json
和 JsonConverter
,我看不出有什么办法可以做到这一点。看来我必须执行 .Read()
来给出下一个标记(StartObject
、EndObject
、StartArray
、... String
等)。
我看到 reader.GetString()
的设施可以提取值,(与 .GetInt32()
和其他人相同),但无法 读取下一个对象 。也就是说,如果我确定 属性 的值是同一 class 的另一个实例,我想完整地读取该对象并将其分配给 属性.
我的 Newtonsoft.Json 转换器如下(请注意,当它在 Left
或 [=31= 中遇到相同 class 的实例时,它会递归调用反序列化方法] 属性):
public class ExpressionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Expression));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
Expression ret = new Expression();
JObject jo = JObject.Load(reader);
if (jo["Left"] != null)
{
JToken leftToken = jo["Left"];
if (leftToken.Type == JTokenType.Object)
{
ret.Left = SerializationHelper.DeserializeJson<Expression>(leftToken.ToString());
}
else if (leftToken.Type == JTokenType.Array)
{
ret.Left = leftToken.ToObject<List<object>>();
}
else
{
ret.Left = leftToken.ToObject<string>();
}
}
ret.Operator = (OperatorEnum)(Enum.Parse(typeof(OperatorEnum), jo["Operator"].ToString()));
if (jo["Right"] != null)
{
JToken rightToken = jo["Right"];
if (rightToken.Type == JTokenType.Object)
{
ret.Right = SerializationHelper.DeserializeJson<Expression>(rightToken.ToString());
}
else if (rightToken.Type == JTokenType.Array)
{
ret.Right = rightToken.ToObject<List<object>>();
}
else
{
ret.Right = rightToken.ToObject<string>();
}
}
return ret;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
我的问题是:我可以读出整个对象(一些标记系列)以分配给 Left
或 Right
属性 吗?
编辑:更多上下文。 Expression
class 是:
public class Expression
{
public object Left { get; set; } = null;
public OperatorEnum Operator { get; set; } = OperatorEnum.Equals;
public object Right { get; set; } = null;
}
并且输入 JSON 看起来像这样简单:
{
"Left": "Amount",
"Operator": "GreaterThan",
"Right": 100.00
}
或complex/nested喜欢:
{
"Left": {
"Left": {
"Left": "Amount",
"Operator": "GreaterThan",
"Right": 100.00
},
"Operator": "And",
"Right": {
"Left": "NameOnCard",
"Operator": "Contains",
"Right": "Smith"
}
},
"Operator": "And",
"Right": {
"Left": {
"Left": "Brand",
"Operator": "Equals",
"Right": "Visa"
},
"Operator": "Or",
"Right": {
"Left": "Brand",
"Operator": "Equals",
"Right": "Mastercard"
}
}
}
与 Newtonsoft.Json 一样,您似乎可以递归调用 JsonSerializer.Deserialize
并将 ref Utf8JsonReader
与自定义序列化程序一起作为参数传递。
这是自定义序列化程序中的代码:
if (reader.TokenType == JsonTokenType.StartObject)
{
ret.Right = JsonSerializer.Deserialize<Expression>(ref reader, new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = { new JsonStringEnumConverter(), new ExpressionConverter() },
});
}
反序列化器将在递归停止的地方继续。工作得很好。
我有一个包含通用 (object
) 的 class,可以用 string
、int
、bool
或相同 class 的另一个实例。以前,在使用 Newtonsoft.Json 时,我有一个自定义转换器,它会根据 JToken.Type
是否为 JTokenType.Object
来确定如何反序列化(检测该值可能是相同 [= 的另一个实例) 56=]).
使用 System.Text.Json
和 JsonConverter
,我看不出有什么办法可以做到这一点。看来我必须执行 .Read()
来给出下一个标记(StartObject
、EndObject
、StartArray
、... String
等)。
我看到 reader.GetString()
的设施可以提取值,(与 .GetInt32()
和其他人相同),但无法 读取下一个对象 。也就是说,如果我确定 属性 的值是同一 class 的另一个实例,我想完整地读取该对象并将其分配给 属性.
我的 Newtonsoft.Json 转换器如下(请注意,当它在 Left
或 [=31= 中遇到相同 class 的实例时,它会递归调用反序列化方法] 属性):
public class ExpressionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Expression));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
Expression ret = new Expression();
JObject jo = JObject.Load(reader);
if (jo["Left"] != null)
{
JToken leftToken = jo["Left"];
if (leftToken.Type == JTokenType.Object)
{
ret.Left = SerializationHelper.DeserializeJson<Expression>(leftToken.ToString());
}
else if (leftToken.Type == JTokenType.Array)
{
ret.Left = leftToken.ToObject<List<object>>();
}
else
{
ret.Left = leftToken.ToObject<string>();
}
}
ret.Operator = (OperatorEnum)(Enum.Parse(typeof(OperatorEnum), jo["Operator"].ToString()));
if (jo["Right"] != null)
{
JToken rightToken = jo["Right"];
if (rightToken.Type == JTokenType.Object)
{
ret.Right = SerializationHelper.DeserializeJson<Expression>(rightToken.ToString());
}
else if (rightToken.Type == JTokenType.Array)
{
ret.Right = rightToken.ToObject<List<object>>();
}
else
{
ret.Right = rightToken.ToObject<string>();
}
}
return ret;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
我的问题是:我可以读出整个对象(一些标记系列)以分配给 Left
或 Right
属性 吗?
编辑:更多上下文。 Expression
class 是:
public class Expression
{
public object Left { get; set; } = null;
public OperatorEnum Operator { get; set; } = OperatorEnum.Equals;
public object Right { get; set; } = null;
}
并且输入 JSON 看起来像这样简单:
{
"Left": "Amount",
"Operator": "GreaterThan",
"Right": 100.00
}
或complex/nested喜欢:
{
"Left": {
"Left": {
"Left": "Amount",
"Operator": "GreaterThan",
"Right": 100.00
},
"Operator": "And",
"Right": {
"Left": "NameOnCard",
"Operator": "Contains",
"Right": "Smith"
}
},
"Operator": "And",
"Right": {
"Left": {
"Left": "Brand",
"Operator": "Equals",
"Right": "Visa"
},
"Operator": "Or",
"Right": {
"Left": "Brand",
"Operator": "Equals",
"Right": "Mastercard"
}
}
}
与 Newtonsoft.Json 一样,您似乎可以递归调用 JsonSerializer.Deserialize
并将 ref Utf8JsonReader
与自定义序列化程序一起作为参数传递。
这是自定义序列化程序中的代码:
if (reader.TokenType == JsonTokenType.StartObject)
{
ret.Right = JsonSerializer.Deserialize<Expression>(ref reader, new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = { new JsonStringEnumConverter(), new ExpressionConverter() },
});
}
反序列化器将在递归停止的地方继续。工作得很好。