System.Text.Json反序列化:处理属性类型不一致并配置回退逻辑
处理System.Text.Json反序列化时的多类型属性回退问题
问题场景
调用外部API返回列表时,列表项的location属性可能是字符串,也可能是对象。需要把对象转成Location实例,字符串则把该属性设为null,同时不能抛出System.Text.Json.JsonException异常。
相关代码定义:
public class ListItem { [JsonPropertyName("location")] public Location Location { get;set; } } public class Location { [JsonPropertyName("city")] public string City { get; set; } [JsonPropertyName("country")] public string Country { get; set; } }
API返回示例:
[ { "location": "some location", // 其他属性... }, { "location": { "city": "some city", "country": "some country" } } ]
解决方案:自定义Json转换器
核心思路是给Location类型写一个自定义转换器,在反序列化时判断JSON token的类型,根据类型决定返回Location实例还是null。
自定义转换器代码
using System.Text.Json; using System.Text.Json.Serialization; public class LocationConverter : JsonConverter<Location> { public override Location Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 检查当前JSON的token类型 if (reader.TokenType == JsonTokenType.String) { // 跳过字符串值,直接返回null reader.Skip(); return null; } else if (reader.TokenType == JsonTokenType.StartObject) { // 正常反序列化为Location对象 return JsonSerializer.Deserialize<Location>(ref reader, options); } // 其他类型(比如数字、数组)也返回null,按需调整 reader.Skip(); return null; } public override void Write(Utf8JsonWriter writer, Location value, JsonSerializerOptions options) { // 如果不需要序列化Location对象,这里可以抛出NotImplementedException JsonSerializer.Serialize(writer, value, options); } }
应用转换器的两种方式
方式1:属性标记
直接在ListItem的Location属性上加特性,只对这个属性生效:
public class ListItem { [JsonPropertyName("location")] [JsonConverter(typeof(LocationConverter))] public Location Location { get;set; } }
然后正常反序列化:
var jsonStr = "API返回的JSON字符串"; var itemList = JsonSerializer.Deserialize<List<ListItem>>(jsonStr);
方式2:全局配置
如果多个地方都要处理Location类型,可以全局添加转换器:
var serializeOptions = new JsonSerializerOptions(); serializeOptions.Converters.Add(new LocationConverter()); var jsonStr = "API返回的JSON字符串"; var itemList = JsonSerializer.Deserialize<List<ListItem>>(jsonStr, serializeOptions);
这样处理后,遇到location是字符串时,ListItem.Location会被设为null;是对象时则正常转成Location实例,不会抛出异常。
内容的提问来源于stack exchange,提问作者user2088880
相关产品推荐
相关产品推荐

