简体   繁体   中英

Decode JSON array without keys in Swift

I make a request to the OpenSky Network API and get a result like this:

{
  "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. From the API documentation (see link above) I know what value corresponds to what property, but how do I create corresponding Swift objects?

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. So how do I tell Swift to interpret the values in the states lists as properties of objects with their corresponding type?

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.

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.

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 .

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)
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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