简体   繁体   English

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

[英]Decode JSON array without keys in Swift

I make a request to the OpenSky Network API and get a result like this:我向OpenSky 网络 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]
  ]
}

Now I want to decode that JSON string to a list of FlightState objects, but there are no keys provided.现在我想将 JSON 字符串解码为FlightState对象列表,但没有提供键。 From the API documentation (see link above) I know what value corresponds to what property, but how do I create corresponding Swift objects?从 API 文档(见上面的链接)我知道什么值对应什么属性,但是我如何创建相应的 Swift 对象?

The following doesn't work, obviously, because there are no keys.显然,以下内容不起作用,因为没有键。

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
    }

}

The error message says Expected to decode Dictionary<String, Any> but found an array instead.错误消息显示Expected to decode Dictionary<String, Any> but found an array instead. So how do I tell Swift to interpret the values in the states lists as properties of objects with their corresponding type?那么我如何告诉 Swift 将states列表中的值解释为具有相应类型的对象的属性?

I have the feeling that someone must have been stumbled upon this before, but I couldn't find a solution.我觉得以前一定有人偶然发现了这个,但我找不到解决办法。 Maybe I am too new to Swift to recognize a solution as such.也许我对 Swift 太陌生了,无法识别这样的解决方案。

You can decode a JSON where specific properties are held in specific array indices rather than under specific Dictionary keys by implementing init(from decoder:) on your own and using an unkeyedContainer() to decode the array, then calling decode to decode each property in order.您可以解码 JSON,其中特定属性保存在特定数组索引中而不是特定Dictionary键下,方法是自行实现init(from decoder:)并使用unkeyedContainer()解码数组,然后调用decode解码中的每个属性命令。

Bear in mind lots of properties in the response can be null , so you need to declare those as Optional and use decodeIfPresent rather than 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)
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM