在 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)
}
}
}
我向 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)
}
}
}