Reputation: 780
Im learning about API's and Decodable currently and for practice, I wanted to attempt to print out the name of every event on Ticketmaster using their api. With every attempt to, Im getting this error presented to me:
Error serializing JSON: typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))
What Im trying to do is print out just the names of the events in the debug area but nothing is showing up. I have a struct following the decodable protocol named Events:
struct Event: Decodable {
let name: String? }
And my attempt to request the information from Ticketmaster using my API key:
//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()
What am I doing wrong? Its grabbing the data correctly, which I can tell because of the print statement I have (print(dataAsString)), but when I just want to display the names, Im getting the error.
Here's an Image of the data structure for reference. Is it because of the embedded part? Any assistance would really be appreciated!
Upvotes: 0
Views: 965
Reputation: 1604
I generated Codables
for the TicketMaster API response using quicktype:
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()
}
}
You'll see that the top-level type of the JSON response is not [Event]
, but an object of type TicketMaster
, which has an [Event]
value nested at .embedded.events
. Here's how to get the names of all the events:
let ticketMaster = TicketMaster.from(json: dataAsString)!
let eventNames = ticketMaster.embedded.events.map { $0.name }
Upvotes: 2