Codable 错误 - 预期解码字典 <String, Any> 但找到数组

Codable Error - Expected to decode Dictionary <String, Any> but found array

请有人帮我指出哪里做错了。我已经搜索过其他类似的问题,但没有一个似乎有助于解决这个问题 我正在尝试使用 Swift Codable

解码 json

我已经创建了嵌套的可编码结构,但它总是失败。

我正在尝试解码的 json 可以在这里找到 - https://pastebin.com/C0QCREEF

这是失败的可编码结构 -

struct OutboundFlightData: Codable {
  var data: OutboundFlight
}
struct OutboundFlight: Codable {
  var outboundFlights: [OutboundFlightDetails]
}
struct OutboundFlightDetails: Codable {
  var amount: String
  var fareFamily: Bool
  var cabin: String
  var fareBasis: String
  var duration: String
  var itinerary: [OutboundFlightItinerary]
}
struct OutboundFlightItinerary: Codable {
  var timeOfDeparture: String
  var timeOfArrival: String
  var dateOfDeparture: String
  var dateOfArrival: String
  var departureLocation: String
  var departureDetails: FlightItineraryDetails
  var arrivalLocation: String
  var arrivalDetails: [FlightItineraryDetails]
  var departureTerminal: String?
  var arrivalTerminal: String?
  var operatingCompany: String
  var operatingDetails: FlightItineraryOperatingDetails
  var marketingCompany: String
  var marketingDetails: FlightItineraryOperatingDetails
  var flightNumber: String
 var electronicTicketing: String
 }
 struct FlightItineraryOperatingDetails: Codable {
   var iata: String
   var fullName: String
   var countryName: String
   var logoSmall: String
   var logo: String
  enum CodingKeys: String, CodingKey {
   case iata
   case fullName = "full_name"
   case logoSmall = "logo_small"
   case logo
   case countryName = "country_name"
  }
 }
 struct FlightItineraryDetails: Codable {
   var iata: String
   var name: String
   var cityName: String
   var stateName: String
   var countryName: String
  enum CodingKeys: String, CodingKey {
   case iata
   case name
   case cityName = "city_name"
   case stateName = "state_name"
   case countryName = "country_name"
 }
}

我希望 json 能够成功解码。但是我一直收到错误 -

typeMismatch(
    Swift.Array<Any>,
    Swift.DecodingError.Context(
        codingPath: [
            CodingKeys(stringValue: "data", intValue: nil),
            CodingKeys(stringValue: "outboundFlights", intValue: nil),
              _JSONKey(stringValue: "Index 0", intValue: 0),
            CodingKeys(stringValue: "itinerary", intValue: nil),
              _JSONKey(stringValue: "Index 0", intValue: 0),
            CodingKeys(stringValue: "arrivalDetails", intValue: nil)
        ],
        debugDescription: "Expected to decode Array<Any> but found a dictionary instead.",
        underlyingError: nil
    )
)

但是当我从数组中更改 arrivalDetails 时。我得到这个错误

typeMismatch(
    Swift.Dictionary<Swift.String, Any>,
    Swift.DecodingError.Context(
        codingPath: [
            CodingKeys(stringValue: "data", intValue: nil),
            CodingKeys(stringValue: "outboundFlights", intValue: nil), 
              _JSONKey(stringValue: "Index 6", intValue: 6),
            CodingKeys(stringValue: "itinerary", intValue: nil),
              _JSONKey(stringValue: "Index 1", intValue: 1),
            CodingKeys(stringValue: "arrivalDetails", intValue: nil)
        ],
        debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.",
        underlyingError: nil
    )
)

从您提供的示例 JSON 看来,arrivalDetails 字段是用字典或空数组填充的。这就是为什么当您在 [FlightItineraryDetails]FlightItineraryDetails 之间切换时,无论哪种方式都会出错,因为解码器无法将字典解析为数组,或将数组解析为字典。

不幸的是,Codable 无法立即处理这种情况。您需要为这种情况创建一个枚举,尝试以两种方式解析该键,返回它在数据中找到的任何一个。

(此代码无耻地从将 JSON 馈送到 https://app.quicktype.io 的输出中提取出来。我总是建议使用它来将 JSON 个示例从 API 映射到可编码结构。它至少让你完成了 80% 的事情,并处理了像这样 API 表现不佳的奇怪情况。)

enum ArrivalDetails: Codable {
    case anythingArray([JSONAny])
    case details(Details)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode([JSONAny].self) {
            self = .anythingArray(x)
            return
        }
        if let x = try? container.decode(Details.self) {
            self = .details(x)
            return
        }
        throw DecodingError.typeMismatch(ArrivalDetails.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ArrivalDetails"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .anythingArray(let x):
            try container.encode(x)
        case .details(let x):
            try container.encode(x)
        }
    }
}

如果您可以控制 returns 这个结果的 API,您可以考虑将该字段更改为不存在,或者如果该字段应为空则设置为 null,而不是一个空数组。然后你可以简单地把它改回 [FlightItineraryDetails]?.