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

时间:2019-09-03 22:15:31

标签: ios swift codable

请有人帮我指出做错了什么。我已经搜索了类似的其他类似问题,但似乎都不能帮助解决此问题 我正在尝试使用Swift Codable解码JSON

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

要解码的json可以在这里找到-https://pastebin.com/C0QCREEF

这是失败的可编码结构-

let closed = false;
function* syncGenerator() {
  yield* ~20-trillion-calculated-results;
}

function main( generator = syncGenerator() ) 
{
    if (closed) 
    {
      syncGracefulCleanup();
    }
    else 
    {
        // assuming syncWork returns false when 
        // there are no more value
        if ( syncWork( generator.next() ) )
        {
            // next-ish off to the event loop. this is NOT recursive, 
            // even though it may look a bit like it
            setTimeout( () => main(generator), 0 );         
        }
    }
}

process.on('SIGINT', () => close = true);

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

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

但是当我从array更改到达细节时。我收到此错误

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

1 个答案:

答案 0 :(得分:1)

从您提供的示例JSON来看,到达详细信息字段正在用字典或空数组填充。这就是为什么当您在[FlightItineraryDetails]FlightItineraryDetails之间切换时,由于解码器无法将字典解析为数组或将数组解析为字典而导致错误的原因。

不幸的是,这是Codable无法开箱即用的情况。您需要为这种情况创建一个枚举,尝试以两种方式解析该键,并返回在数据中找到的那个键。

(此代码是从将JSON馈入https://app.quicktype.io的输出中无耻提取的。我总是建议使用该代码将JSON示例从API映射到Codable结构中。它至少使您获得了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)
        }
    }
}

如果您控制返回此结果的API,则可以考虑将该字段更改为不存在,或者如果该字段应为空而不是空数组,则将其设置为null。然后,您只需将其更改回[FlightItineraryDetails]?