C# System.Text.Json无法反序列化OpenSky API响应,DTO模型不匹配求助
问题根源分析
你遇到的核心问题是JSON结构与DTO模型不匹配:OpenSky API返回的states是纯值类型的二维数组(每个子数组是顺序排列的原始值),但你试图用带[JsonPropertyName("0")]这类索引属性的类来映射数组元素——System.Text.Json默认不支持将数组元素直接映射到类的属性上,这才导致反序列化失败,最终实例为null。
解决方案
下面提供两种可行的解决方式,推荐第一种(自定义转换器),更符合面向对象的代码风格:
方案一:自定义JsonConverter处理数组到对象的映射
1. 编写自定义转换器
这个转换器会读取JSON数组的每个元素,按顺序赋值给FlightStatusOpenSkyDto的属性:
public class FlightStatusOpenSkyDtoConverter : JsonConverter<FlightStatusOpenSkyDto> { public override FlightStatusOpenSkyDto? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartArray) return null; var dto = new FlightStatusOpenSkyDto(); int index = 0; while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndArray) break; switch (index) { case 0: dto.Icao24 = reader.TokenType == JsonTokenType.Null ? null : reader.GetString(); break; case 1: dto.Callsign = reader.TokenType == JsonTokenType.Null ? null : reader.GetString()?.Trim(); break; case 2: dto.OriginCountry = reader.TokenType == JsonTokenType.Null ? null : reader.GetString(); break; case 3: dto.TimePosition = reader.TokenType == JsonTokenType.Null ? null : reader.GetInt32(); break; case 4: dto.LastContact = reader.TokenType == JsonTokenType.Null ? null : reader.GetInt32(); break; case 5: dto.Longitude = reader.TokenType == JsonTokenType.Null ? null : (float?)reader.GetDouble(); break; case 6: dto.Latitude = reader.TokenType == JsonTokenType.Null ? null : (float?)reader.GetDouble(); break; case 7: dto.BaroAltitude = reader.TokenType == JsonTokenType.Null ? null : (float?)reader.GetDouble(); break; case 8: dto.OnGround = reader.TokenType == JsonTokenType.Null ? null : reader.GetBoolean(); break; case 9: dto.Velocity = reader.TokenType == JsonTokenType.Null ? null : (float?)reader.GetDouble(); break; case 10: dto.TrueTrack = reader.TokenType == JsonTokenType.Null ? null : (float?)reader.GetDouble(); break; case 11: dto.VerticalRate = reader.TokenType == JsonTokenType.Null ? null : (float?)reader.GetDouble(); break; case 12: dto.Sensors = reader.TokenType == JsonTokenType.Null ? null : JsonSerializer.Deserialize<int[]>(ref reader, options); break; case 13: dto.GeoAltitude = reader.TokenType == JsonTokenType.Null ? null : (float?)reader.GetDouble(); break; case 14: dto.Squawk = reader.TokenType == JsonTokenType.Null ? null : reader.GetString(); break; case 15: dto.Spi = reader.TokenType == JsonTokenType.Null ? null : reader.GetBoolean(); break; case 16: dto.PositionSource = reader.TokenType == JsonTokenType.Null ? null : reader.GetInt32(); break; case 17: dto.Category = reader.TokenType == JsonTokenType.Null ? null : reader.GetInt32(); break; } index++; } return dto; } public override void Write(Utf8JsonWriter writer, FlightStatusOpenSkyDto value, JsonSerializerOptions options) { // 若不需要序列化DTO,可留空;如需实现,按数组顺序写入对应值即可 writer.WriteStartArray(); writer.WriteStringValue(value.Icao24); writer.WriteStringValue(value.Callsign); writer.WriteStringValue(value.OriginCountry); writer.WriteNumberValue(value.TimePosition ?? 0); writer.WriteNumberValue(value.LastContact ?? 0); writer.WriteNumberValue(value.Longitude ?? 0); writer.WriteNumberValue(value.Latitude ?? 0); writer.WriteNumberValue(value.BaroAltitude ?? 0); writer.WriteBooleanValue(value.OnGround ?? false); writer.WriteNumberValue(value.Velocity ?? 0); writer.WriteNumberValue(value.TrueTrack ?? 0); writer.WriteNumberValue(value.VerticalRate ?? 0); if (value.Sensors != null) JsonSerializer.Serialize(writer, value.Sensors, options); else writer.WriteNullValue(); writer.WriteNumberValue(value.GeoAltitude ?? 0); writer.WriteStringValue(value.Squawk); writer.WriteBooleanValue(value.Spi ?? false); writer.WriteNumberValue(value.PositionSource ?? 0); writer.WriteNumberValue(value.Category ?? 0); writer.WriteEndArray(); } }
2. 修改DTO模型
移除原来的[JsonPropertyName]索引属性,给FlightStatusOpenSkyDto添加转换器标记:
public class RootObjectOfFlightStatus { [JsonPropertyName("time")] public long Time { get; set; } [JsonPropertyName("states")] public List<FlightStatusOpenSkyDto>? States { get; set; } } [JsonConverter(typeof(FlightStatusOpenSkyDtoConverter))] public class FlightStatusOpenSkyDto { public string? Icao24 { get; set; } public string? Callsign { get; set; } public string? OriginCountry { get; set; } public int? TimePosition { get; set; } public int? LastContact { get; set; } public float? Longitude { get; set; } public float? Latitude { get; set; } public float? BaroAltitude { get; set; } public bool? OnGround { get; set; } public float? Velocity { get; set; } public float? TrueTrack { get; set; } public float? VerticalRate { get; set; } public int[]? Sensors { get; set; } public float? GeoAltitude { get; set; } public string? Squawk { get; set; } public bool? Spi { get; set; } public int? PositionSource { get; set; } public int? Category { get; set; } }
3. 反序列化时传入转换器选项
var options = new JsonSerializerOptions(); options.Converters.Add(new FlightStatusOpenSkyDtoConverter()); RootObjectOfFlightStatus? flightStatusOpenSkyDto = JsonSerializer.Deserialize<RootObjectOfFlightStatus>(response, options);
方案二:直接映射原始数组(快速实现)
如果不想编写自定义转换器,可以先将states反序列化为原始数组,再手动映射到DTO:
// 修改RootObject模型 public class RootObjectOfFlightStatus { [JsonPropertyName("time")] public long Time { get; set; } [JsonPropertyName("states")] public List<object[]>? States { get; set; } } // 反序列化后手动转换 var rawData = JsonSerializer.Deserialize<RootObjectOfFlightStatus>(response); var flightStatusList = rawData?.States?.Select(arr => new FlightStatusOpenSkyDto { Icao24 = arr[0] as string, Callsign = (arr[1] as string)?.Trim(), OriginCountry = arr[2] as string, TimePosition = arr[3] as int?, LastContact = arr[4] as int?, Longitude = arr[5] as float?, Latitude = arr[6] as float?, BaroAltitude = arr[7] as float?, OnGround = arr[8] as bool?, Velocity = arr[9] as float?, TrueTrack = arr[10] as float?, VerticalRate = arr[11] as float?, Sensors = arr[12] as int[], GeoAltitude = arr[13] as float?, Squawk = arr[14] as string, Spi = arr[15] as bool?, PositionSource = arr[16] as int?, Category = arr[17] as int? }).ToList();
内容的提问来源于stack exchange,提问作者Филипп Демиденко
相关产品推荐
相关产品推荐

