从TicketMasters API中提取数据(Swift 4.0 / Decodable)

时间:2017-10-13 22:32:28

标签: arrays json swift api dictionary

我正在学习API和Decodable目前和实践,我想尝试使用他们的api在Ticketmaster上打印出每个事件的名称。每次尝试,我都会向我提出这个错误:

Error serializing JSON: typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))

我想要做的是打印出调试区域中事件的名称,但没有显示任何内容。我有一个遵循名为Events的可解码协议的结构:

struct Event: Decodable {
let name: String? }

我尝试使用我的API密钥从Ticketmaster请求信息:

//API String Request
    let jsonUrlString = "https://app.ticketmaster.com/discovery/v2/events.json?countryCode=US&apikey=zqqqmkCdkfslHeaCvqXbxQZFGNXHoAT2"
    guard let url = URL(string: jsonUrlString) else { return }

    //Method to pull information from Ticketmasters API
    URLSession.shared.dataTask(with: url) { (data, response, err) in
        guard let data = data else { return }
        let dataAsString = String(data: data, encoding: .utf8)
        print(dataAsString)

        do {
            let events = try JSONDecoder().decode([Event].self, from: data)
            print(events)
            print()
            print("Done.")
        } catch let jsonErr {
            print("Error serializing JSON:", jsonErr)
        }
    }.resume()

我做错了什么?它正确抓取数据,我可以告诉我因为我有的print语句(print(dataAsString)),但是当我只想显示名称时,我得到了错误。

这里是数据结构的图像供参考。是因为嵌入式部分?真的很感激任何帮助!

enter image description here

1 个答案:

答案 0 :(得分:3)

我使用quicktype生成Codables TicketMaster API响应:

typealias TicketMaster = OtherTicketMaster

struct OtherTicketMaster: Codable {
    let links: OtherOtherLinks
    let embedded: Embedded
    let page: Page
}

struct OtherOtherLinks: Codable {
    let last: OtherAttraction
    let first: OtherAttraction
    let next: OtherAttraction
    let otherSelf: OtherAttraction
}

struct OtherAttraction: Codable {
    let href: String
}

struct Embedded: Codable {
    let events: [Event]
}

struct Event: Codable {
    let info: String?
    let classifications: [Classification]
    let links: OtherLinks
    let embedded: OtherEmbedded
    let accessibility: Accessibility?
    let id: String
    let dates: Dates
    let images: [Image]
    let priceRanges: [PriceRange]
    let sales: Sales
    let name: String
    let locale: String
    let pleaseNote: String?
    let promoter: Promoter
    let products: [Products]?
    let promoters: [Promoter]
    let test: Bool
    let seatmap: Seatmap
    let type: String
    let url: String
}

struct Classification: Codable {
    let primary: Bool
    let subGenre: Genre
    let genre: Genre
    let segment: Genre
    let subType: Genre
    let type: Genre
}

struct Genre: Codable {
    let id: String
    let name: String
}

struct OtherLinks: Codable {
    let otherSelf: OtherAttraction
    let attractions: [OtherAttraction]
    let venues: [OtherAttraction]
}

struct OtherEmbedded: Codable {
    let attractions: [Attraction]
    let venues: [Venue]
}

struct Attraction: Codable {
    let images: [Image]
    let classifications: [Classification]
    let links: Links
    let id: String
    let name: String
    let type: String
    let locale: String
    let test: Bool
    let upcomingEvents: UpcomingEvents
    let url: String
}

struct Links: Codable {
    let otherSelf: OtherAttraction
}

struct UpcomingEvents: Codable {
    let ticketmaster: Int?
    let total: Int
    let tmr: Int?
}

struct Venue: Codable {
    let generalInfo: GeneralInfo?
    let postalCode: String
    let boxOfficeInfo: BoxOfficeInfo?
    let accessibleSeatingDetail: String?
    let links: Links
    let address: Address
    let country: Country
    let city: City
    let dmas: [Dma]
    let location: Location
    let images: [Image]?
    let id: String
    let locale: String
    let name: String
    let markets: [Market]
    let parkingDetail: String
    let timezone: String
    let state: State
    let social: Social?
    let test: Bool
    let upcomingEvents: UpcomingEvents
    let type: String
    let url: String
}

struct GeneralInfo: Codable {
    let childRule: String?
    let generalRule: String
}

struct BoxOfficeInfo: Codable {
    let openHoursDetail: String
    let acceptedPaymentDetail: String
    let phoneNumberDetail: String?
    let willCallDetail: String
}

struct Address: Codable {
    let line1: String
}

struct Country: Codable {
    let countryCode: String
    let name: String
}

struct City: Codable {
    let name: String
}

struct Dma: Codable {
    let id: Int
}

struct Location: Codable {
    let latitude: String
    let longitude: String
}

struct Market: Codable {
    let id: String
}

struct State: Codable {
    let name: String
    let stateCode: String
}

struct Social: Codable {
    let twitter: Twitter
}

struct Twitter: Codable {
    let handle: String
}

struct Accessibility: Codable {
    let info: String
}

struct Dates: Codable {
    let start: Start
    let spanMultipleDays: Bool
    let status: Status
    let timezone: String
}

struct Start: Codable {
    let localDate: String
    let dateTBD: Bool
    let dateTBA: Bool
    let dateTime: String
    let noSpecificTime: Bool
    let localTime: String
    let timeTBA: Bool
}

struct Status: Codable {
    let code: String
}

struct Image: Codable {
    let fallback: Bool
    let ratio: String?
    let attribution: String?
    let height: Int
    let url: String
    let width: Int
}

struct PriceRange: Codable {
    let max: Double
    let currency: String
    let min: Double
    let type: String
}

struct Sales: Codable {
    let presales: [Presales]?
    let otherPublic: Public
}

struct Presales: Codable {
    let endDateTime: String
    let startDateTime: String
    let description: String?
    let name: String
    let url: String?
}

struct Public: Codable {
    let startDateTime: String
    let endDateTime: String
    let startTBD: Bool
}

struct Promoter: Codable {
    let id: String
    let description: String
    let name: String
}

struct Products: Codable {
    let name: String
    let id: String
    let type: String
    let url: String
}

struct Seatmap: Codable {
    let staticUrl: String
}

struct Page: Codable {
    let size: Int
    let number: Int
    let totalElements: Int
    let totalPages: Int
}

// Serialization extensions

extension OtherTicketMaster {
    static func from(json: String, using encoding: String.Encoding = .utf8) -> OtherTicketMaster? {
        guard let data = json.data(using: encoding) else { return nil }
        return OtherTicketMaster.from(data: data)
    }

    static func from(data: Data) -> OtherTicketMaster? {
        let decoder = JSONDecoder()
        return try? decoder.decode(OtherTicketMaster.self, from: data)
    }

    var jsonData: Data? {
        let encoder = JSONEncoder()
        return try? encoder.encode(self)
    }

    var jsonString: String? {
        guard let data = self.jsonData else { return nil }
        return String(data: data, encoding: .utf8)
    }
}

extension Accessibility {
    enum CodingKeys: String, CodingKey {
        case info
    }
}

extension Address {
    enum CodingKeys: String, CodingKey {
        case line1
    }
}

extension Attraction {
    enum CodingKeys: String, CodingKey {
        case images
        case classifications
        case links = "_links"
        case id
        case name
        case type
        case locale
        case test
        case upcomingEvents
        case url
    }
}

extension BoxOfficeInfo {
    enum CodingKeys: String, CodingKey {
        case openHoursDetail
        case acceptedPaymentDetail
        case phoneNumberDetail
        case willCallDetail
    }
}

extension City {
    enum CodingKeys: String, CodingKey {
        case name
    }
}

extension Classification {
    enum CodingKeys: String, CodingKey {
        case primary
        case subGenre
        case genre
        case segment
        case subType
        case type
    }
}

extension Country {
    enum CodingKeys: String, CodingKey {
        case countryCode
        case name
    }
}

extension Dates {
    enum CodingKeys: String, CodingKey {
        case start
        case spanMultipleDays
        case status
        case timezone
    }
}

extension Dma {
    enum CodingKeys: String, CodingKey {
        case id
    }
}

extension Embedded {
    enum CodingKeys: String, CodingKey {
        case events
    }
}

extension Event {
    enum CodingKeys: String, CodingKey {
        case info
        case classifications
        case links = "_links"
        case embedded = "_embedded"
        case accessibility
        case id
        case dates
        case images
        case priceRanges
        case sales
        case name
        case locale
        case pleaseNote
        case promoter
        case products
        case promoters
        case test
        case seatmap
        case type
        case url
    }
}

extension GeneralInfo {
    enum CodingKeys: String, CodingKey {
        case childRule
        case generalRule
    }
}

extension Genre {
    enum CodingKeys: String, CodingKey {
        case id
        case name
    }
}

extension Image {
    enum CodingKeys: String, CodingKey {
        case fallback
        case ratio
        case attribution
        case height
        case url
        case width
    }
}

extension Links {
    enum CodingKeys: String, CodingKey {
        case otherSelf = "self"
    }
}

extension Location {
    enum CodingKeys: String, CodingKey {
        case latitude
        case longitude
    }
}

extension Market {
    enum CodingKeys: String, CodingKey {
        case id
    }
}

extension OtherAttraction {
    enum CodingKeys: String, CodingKey {
        case href
    }
}

extension OtherEmbedded {
    enum CodingKeys: String, CodingKey {
        case attractions
        case venues
    }
}

extension OtherLinks {
    enum CodingKeys: String, CodingKey {
        case otherSelf = "self"
        case attractions
        case venues
    }
}

extension OtherOtherLinks {
    enum CodingKeys: String, CodingKey {
        case last
        case first
        case next
        case otherSelf = "self"
    }
}

extension OtherTicketMaster {
    enum CodingKeys: String, CodingKey {
        case links = "_links"
        case embedded = "_embedded"
        case page
    }
}

extension Page {
    enum CodingKeys: String, CodingKey {
        case size
        case number
        case totalElements
        case totalPages
    }
}

extension Presales {
    enum CodingKeys: String, CodingKey {
        case endDateTime
        case startDateTime
        case description
        case name
        case url
    }
}

extension PriceRange {
    enum CodingKeys: String, CodingKey {
        case max
        case currency
        case min
        case type
    }
}

extension Products {
    enum CodingKeys: String, CodingKey {
        case name
        case id
        case type
        case url
    }
}

extension Promoter {
    enum CodingKeys: String, CodingKey {
        case id
        case description
        case name
    }
}

extension Public {
    enum CodingKeys: String, CodingKey {
        case startDateTime
        case endDateTime
        case startTBD
    }
}

extension Sales {
    enum CodingKeys: String, CodingKey {
        case presales
        case otherPublic = "public"
    }
}

extension Seatmap {
    enum CodingKeys: String, CodingKey {
        case staticUrl
    }
}

extension Social {
    enum CodingKeys: String, CodingKey {
        case twitter
    }
}

extension Start {
    enum CodingKeys: String, CodingKey {
        case localDate
        case dateTBD
        case dateTBA
        case dateTime
        case noSpecificTime
        case localTime
        case timeTBA
    }
}

extension State {
    enum CodingKeys: String, CodingKey {
        case name
        case stateCode
    }
}

extension Status {
    enum CodingKeys: String, CodingKey {
        case code
    }
}

extension Twitter {
    enum CodingKeys: String, CodingKey {
        case handle
    }
}

extension UpcomingEvents {
    enum CodingKeys: String, CodingKey {
        case ticketmaster
        case total = "_total"
        case tmr
    }
}

extension Venue {
    enum CodingKeys: String, CodingKey {
        case generalInfo
        case postalCode
        case boxOfficeInfo
        case accessibleSeatingDetail
        case links = "_links"
        case address
        case country
        case city
        case dmas
        case location
        case images
        case id
        case locale
        case name
        case markets
        case parkingDetail
        case timezone
        case state
        case social
        case test
        case upcomingEvents
        case type
        case url
    }
}

// Helpers

class JSONNull: Codable {
    public init() {
    }

    public required init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if !container.decodeNil() {
            throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encodeNil()
    }
}

您会看到JSON响应的顶级类型不是[Event],而是TicketMaster类型的对象,其[Event]值嵌套在{ {1}}。以下是如何获取所有事件的名称:

.embedded.events