Swift 4借助第三方库解析Yelp API JSON数据问题求助
问题描述
我在Xcode项目中使用一款GitHub第三方库,通过它调用Yelp API以在应用内获取搜索结果,目前已成功下载数据并转换为JSON字符串,但卡在了解析该JSON字符串的逻辑上,无法访问name、display_address、price等实际参数。我之前使用过Decode方法,但从未通过第三方库进行此类操作,相信只差一两步,但经验不足导致无法推进,恳请帮助。
以下是获取JSON数据的函数:
func getYelp() { let yelpAPIClient = CDYelpAPIClient(apiKey: "MY_API_KEY") yelpAPIClient.cancelAllPendingAPIRequests() CDYelpFusionKitManager.shared.apiClient = yelpAPIClient CDYelpFusionKitManager.shared.apiClient.searchBusinesses(byTerm: "Bars", location: "Hollywood, California", latitude: nil, longitude: nil, radius: 10000, categories: [CDYelpBusinessCategoryFilter.bars], locale: CDYelpLocale.english_unitedStates, limit: 5, offset: 0, sortBy: CDYelpBusinessSortType.distance, priceTiers: [CDYelpPriceTier.twoDollarSigns], openNow: true, openAt: nil, attributes: nil) { (response) in if let response = response, let businesses = response.businesses?.toJSONString(), businesses.count > 0 { print(businesses) } } }
返回的JSON数据:
[ { "name" : "The Know Where Bar", "coordinates" : { "latitude" : 34.101480000000002, "longitude" : -118.3127424 }, "rating" : 4.5, "price" : "$$", "review_count" : 170, "display_phone" : "(323) 871-4108", "location" : { "state" : "CA", "address3" : "", "address1" : "5634 Hollywood Blvd", "city" : "Los Angeles", "display_address" : [ "5634 Hollywood Blvd", "Los Angeles, CA 90028" ], "zip_code" : "90028", "country" : "US" }, "id" : "KOj4TvBuvRLXlBuG2vrZeQ", "categories" : [ { "title" : "Cocktail Bars", "alias" : "cocktailbars" }, { "title" : "Jazz & Blues", "alias" : "jazzandblues" }, { "title" : "Pubs", "alias" : "pubs" } ], "transactions" : [ ], "distance" : 2232.7094070718849, "phone" : "+13238714108", "image_url" : "https:\/\/s3-media3.fl.yelpcdn.com\/bphoto\/Ehus0C-bAoA-6fNbwGpSYA\/o.jpg", "is_closed" : false, "url" : "https:\/\/www.yelp.com\/biz\/the-know-where-bar-los-angeles?adjust_creative=xVfmfDi4k3F3Jm9zaJU1CA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=xVfmfDi4k3F3Jm9zaJU1CA" }, { "name" : "Lost Property Bar", "coordinates" : { "latitude" : 34.1019182, "longitude" : -118.32650390000001 }, "rating" : 4.5, "price" : "$$", "review_count" : 143, "display_phone" : "(323) 987-4445", "location" : { "state" : "CA", "address3" : "", "address1" : "1704 N Vine St", "city" : "Hollywood", "address2" : "", "display_address" : [ "1704 N Vine St", "Hollywood, CA 90028" ], "zip_code" : "90028", "country" : "US" }, "id" : "CBYwPYYEFbpCefJ4rdiyPw", "categories" : [ { "title" : "Pubs", "alias" : "pubs" }, { "title" : "Cocktail Bars", "alias" : "cocktailbars" } ], "transactions" : [ ], "distance" : 1145.5564586880064, "phone" : "+13239874445", "image_url" : "https:\/\/s3-media1.fl.yelpcdn.com\/bphoto\/pswDPAX_3p73JbPEXwThag\/o.jpg", "is_closed" : false, "url" : "https:\/\/www.yelp.com\/biz\/lost-property-bar-hollywood?adjust_creative=xVfmfDi4k3F3Jm9zaJU1CA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=xVfmfDi4k3F3Jm9zaJU1CA" }, { "name" : "Sunset & Vinyl", "coordinates" : { "latitude" : 34.098346200000002, "longitude" : -118.32679779999999 }, "rating" : 4.5, "price" : "$$", "review_count" : 42, "display_phone" : "(424) 646-3375", "location" : { "state" : "CA", "city" : "Los Angeles", "address1" : "1521 Vine St", "address2" : "", "display_address" : [ "1521 Vine St", "Los Angeles, CA 90028" ], "zip_code" : "90028", "country" : "US" }, "id" : "RKSPAI90ITxbnq1-L_F5NA", "categories" : [ { "title" : "Cocktail Bars", "alias" : "cocktailbars" }, { "title" : "Speakeasies", "alias" : "speakeasies" }, { "title" : "Lounges", "alias" : "lounges" } ], "transactions" : [ ], "distance" : 916.67964443671087, "phone" : "+14246463375", "image_url" : "https:\/\/s3-media3.fl.yelpcdn.com\/bphoto\/w91DLpKmUGuXTF-zauYAWA\/o.jpg", "is_closed" : false, "url" : "https:\/\/www.yelp.com\/biz\/sunset-and-vinyl-los-angeles?adjust_creative=xVfmfDi4k3F3Jm9zaJU1CA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=xVfmfDi4k3F3Jm9zaJU1CA" }, { "name" : "A Simple Bar", "coordinates" : { "latitude" : 34.129899999999999, "longitude" : -118.34882 }, "rating" : 5, "price" : "$$", "review_count" : 168, "display_phone" : "(323) 378-5388", "location" : { "state" : "CA", "address3" : "", "address1" : "3256 Cahuenga Blvd W", "city" : "Los Angeles", "display_address" : [ "3256 Cahuenga Blvd W", "Los Angeles, CA 90068" ], "zip_code" : "90068", "country" : "US" }, "id" : "qD3-5yqSyPiz-VpJtPsnkg", "categories" : [ { "title" : "Bars", "alias" : "bars" } ] } ]
解决方案
嘿,你这完全是绕了个没必要的弯路!你用的CDYelpFusionKit这个第三方库已经帮你把Yelp返回的JSON自动解析成模型对象了,根本不需要把它转成JSON字符串再自己解析,这反而给自己添了麻烦。
方法一:直接使用第三方库提供的模型对象
response.businesses本身就是[CDYelpBusiness]类型的数组,直接遍历这个数组就能访问所有你需要的属性了,比如name、price、location?.displayAddress这些。
修改你的getYelp函数就行:
func getYelp() { let yelpAPIClient = CDYelpAPIClient(apiKey: "MY_API_KEY") yelpAPIClient.cancelAllPendingAPIRequests() CDYelpFusionKitManager.shared.apiClient = yelpAPIClient CDYelpFusionKitManager.shared.apiClient.searchBusinesses(byTerm: "Bars", location: "Hollywood, California", latitude: nil, longitude: nil, radius: 10000, categories: [CDYelpBusinessCategoryFilter.bars], locale: CDYelpLocale.english_unitedStates, limit: 5, offset: 0, sortBy: CDYelpBusinessSortType.distance, priceTiers: [CDYelpPriceTier.twoDollarSigns], openNow: true, openAt: nil, attributes: nil) { (response) in // 直接获取CDYelpBusiness数组,不用转JSONString guard let response = response, let businesses = response.businesses else { print("没有获取到商家数据") return } // 遍历每个商家,访问属性 for business in businesses { print("商家名称:\(business.name ?? "未知")") print("价格等级:\(business.price ?? "未知")") // 访问display_address,注意模型里的属性名是displayAddress(驼峰命名) if let displayAddress = business.location?.displayAddress { print("显示地址:\(displayAddress.joined(separator: ", "))") } print("评分:\(business.rating ?? 0)") print("---") } } }
这里注意:第三方库的模型属性是驼峰命名的,比如JSON里的display_address对应模型里的displayAddress,review_count对应reviewCount,别搞混了。
方法二:如果一定要手动解析JSON字符串(不推荐)
如果你因为特殊原因必须用自己的解析逻辑,那可以把JSON字符串转成Data,再用JSONDecoder解码到自定义的模型里:
首先定义你的自定义模型:
// 商家模型 struct Business: Codable { let name: String let price: String? let location: Location let rating: Double // 其他你需要的属性 } // 地址模型 struct Location: Codable { let display_address: [String] // 其他地址属性 }
然后修改解析部分的代码:
if let response = response, let jsonString = response.businesses?.toJSONString(), !jsonString.isEmpty { // 把JSON字符串转成Data guard let jsonData = jsonString.data(using: .utf8) else { print("JSON字符串转Data失败") return } // 解码成自定义模型数组 do { let businesses = try JSONDecoder().decode([Business].self, from: jsonData) // 遍历使用 for business in businesses { print("商家名称:\(business.name)") print("价格:\(business.price ?? "未知")") print("显示地址:\(business.location.display_address.joined(separator: ", "))") } } catch { print("解析失败:\(error.localizedDescription)") } }
总结
优先用方法一!第三方库提供的模型已经帮你处理了所有解析细节,包括可选值、字段映射,比自己手动解析高效得多,也不容易出错。
内容的提问来源于stack exchange,提问作者Coding while Loading

