问题描述
使用 JsonSerialize.DeserializeAsync
和自定义转换器进行反序列化,例如
public class MyStringJsonConverter : JsonConverter<string>
{
public override string Read(ref Utf8JsonReader reader,Type typetoConvert,JsonSerializerOptions options)
{
return reader.GetString();
}
public override void Write(Utf8JsonWriter writer,string value,JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
在这里我会得到 all string
属性,这没什么,但有没有办法检查给定值的属性名称,例如像这样,只处理 Body
属性的位置:
class MyMailContent
{
public string Name { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}
public class MyStringJsonConverter : JsonConverter<string>
{
public override string Read(ref Utf8JsonReader reader,JsonSerializerOptions options)
{
if (reader.PropertyName.Equals("Body"))
{
var s = reader.GetString();
//do some process with the string value
return s;
}
return reader.GetString();
}
}
注意,我正在寻找使用 System.Text.Json
的解决方案。
解决方法
System.Text.Json 不会在 path 内使用父属性名称,或者更一般地说是当前值的 JsonConverter<T>.Read()
。此信息在内部进行跟踪——它在 ReadStack.JsonPath()
中——但 ReadStack
是内部的,永远不会传递给应用程序代码。
但是,如 Registration sample - [JsonConverter] on a property 中所述,您可以使用 JsonConverterAttribute
将 MyStringJsonConverter
直接应用于 public string Body { get; set; }
:
class MyMailContent
{
public string Name { get; set; }
public string Subject { get; set; }
[JsonConverter(typeof(MyStringJsonConverter))]
public string Body { get; set; }
}
这样做后,MyStringJsonConverter.Read()
和 .Write()
将只为 MyMailContent.Body
触发。即使您在 JsonSerializerOptions.Converters
中有一些整体 JsonConverter<string>
,应用于该属性的转换器也会 take precedence:
在序列化或反序列化期间,按以下顺序为每个 JSON 元素选择一个转换器,从最高优先级到最低优先级:
-
[JsonConverter]
应用于属性。 - 添加到
Converters
集合的转换器。 -
[JsonConverter]
应用于自定义值类型或 POCO。
(请注意,这与 Newtonsoft 部分不同,在 {{3}} 中,应用于类型的转换器取代了设置中的转换器。)
,感谢 Jimi 提供 comment,在他的祝福下,我使用他建议的解决方案发布了一个 wiki 答案,任何有更多贡献的人,请随时提供。
>在大多数情况下,接受的答案是一个很好的答案,但如果仍然需要按属性名称处理一个或多个特定属性,这是一种方法。
public class MyMailContent
{
public string Name { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}
public class MyMailContentJsonConverter : JsonConverter<MyMailContent>
{
public override MyMailContent Read(ref Utf8JsonReader reader,Type typeToConvert,JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}
var mailContent = new MyMailContent();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return mailContent;
}
if (reader.TokenType == JsonTokenType.PropertyName)
{
var propertyName = reader.GetString();
reader.Read();
switch (propertyName)
{
case "Name":
mailContent.Name = reader.GetString();
break;
case "Subject":
mailContent.Subject = reader.GetString();
break;
case "Body":
string body = reader.GetString();
//do some process on body here
mailContent.Body = body;
break;
}
}
}
throw new JsonException();
}
public override void Write(Utf8JsonWriter writer,MyMailContent mailcontent,JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
private static JsonSerializerOptions _jsonDeserializeOptions = new()
{
ReadCommentHandling = JsonCommentHandling.Skip,AllowTrailingCommas = true,Converters =
{
new MyMailContentJsonConverter()
}
};
然后像这样使用
var jsonstring = JsonSerializer.Serialize(new MyMailContent
{
Name = "some name",Subject = "some subject",Body = "some body"
});
var MailContent = JsonSerializer.Deserialize<MyMailContent>(jsonstring,_jsonDeserializeOptions);