在 Swift 中解码没有键的 JSON 数组

Decode JSON array without keys in Swift

我向 OpenSky Network API 发出请求并得到如下结果:

{
  "time":1629739170,
  "states":[
    ["4b1800","SWR1076 ","Switzerland",1629739169,1629739169,8.5493,47.4581,373.38,false,79.14,275.97,7.15,null,487.68,null,false,0],
    ["3c65c4","DLH35A  ","Germany",1629739170,1629739170,6.5185,46.2346,11590.02,false,255.57,48.84,0,null,11986.26,"1000",false,0]
  ]
}

现在我想将 JSON 字符串解码为 FlightState 对象列表,但没有提供键。从 API 文档(参见上面的 link)我知道什么值对应什么 属性,但是如何创建相应的 Swift 对象?

显然,以下内容不起作用,因为没有键。

let decoder = JSONDecoder()             
let flightState: FlightState = try! decoder.decode(FlightState.self, from: dataString.data(using: .utf8)!)


struct FlightState: Codable {
    
    let time: Int
    let states: [StateVector]

    struct StateVector: Codable {
        let icao24: UUID
        let callsign: String
        let origin_country: String
        let time_position: Int
        let last_contact: Int
        let longitude: Float
        let latitude: Float
        let baro_altitude: Float
        let on_ground: Bool
        let velocity: Float
        let true_track: Float
        let vertical_rate: Float
        let sensors: [Int]
        let geo_altitude: Float
        let squawk: String
        let spi: Bool
        let position_source: Int
    }

}

错误消息显示 Expected to decode Dictionary<String, Any> but found an array instead. 那么我如何告诉 Swift 将 states 列表中的值解释为具有相应类型的对象的属性?

感觉以前一定有人遇到过这个问题,但我找不到解决办法。也许我对 Swift 太陌生,无法识别这样的解决方案。

您可以通过自己实现 init(from decoder:) 并使用 [=13= 解码 JSON,其中特定属性保存在特定数组索引中而不是特定 Dictionary 键下] 解码数组,然后调用 decode 按顺序解码每个 属性。

请记住,响应中的许多属性可以是 null,因此您需要将它们声明为 Optional 并使用 decodeIfPresent 而不是 decode

struct FlightState: Decodable {

    let time: Int
    let states: [StateVector]

    struct StateVector: Decodable {
        let icao24: String
        let callSign: String
        let originCountry: String
        let timePosition: Int?
        let lastContact: Int
        let longitude: Float?
        let latitude: Float?
        let baroAltitude: Float?
        let onGround: Bool
        let velocity: Float?
        let trueTrack: Float?
        let verticalRate: Float?
        let sensors: [Int]?
        let geoAltitude: Float?
        let squawk: String?
        let spi: Bool
        let positionSource: Int

        init(from decoder: Decoder) throws {
            var values = try decoder.unkeyedContainer()
            self.icao24 = try values.decode(String.self)
            self.callSign = try values.decode(String.self)
            self.originCountry = try values.decode(String.self)
            self.timePosition = try values.decodeIfPresent(Int.self)
            self.lastContact = try values.decode(Int.self)
            self.longitude = try values.decodeIfPresent(Float.self)
            self.latitude = try values.decodeIfPresent(Float.self)
            self.baroAltitude = try values.decodeIfPresent(Float.self)
            self.onGround = try values.decode(Bool.self)
            self.velocity = try values.decodeIfPresent(Float.self)
            self.trueTrack = try values.decodeIfPresent(Float.self)
            self.verticalRate = try values.decodeIfPresent(Float.self)
            self.sensors = try values.decodeIfPresent([Int].self)
            self.geoAltitude = try values.decodeIfPresent(Float.self)
            self.squawk = try values.decodeIfPresent(String.self)
            self.spi = try values.decode(Bool.self)
            self.positionSource = try values.decode(Int.self)
        }
    }
}