解码JSON中的嵌套对象
我正在尝试使用YouTube JSON API获取一些数据,JSON如下所示:解码JSON中的嵌套对象,json,swift,Json,Swift,我正在尝试使用YouTube JSON API获取一些数据,JSON如下所示: { "kind": "youtube#channelListResponse", "etag": "AVEF8yG4pQMKfzZjMyy1rfVtFXA", "pageInfo": { "resultsPerPage": 1 }, "items":
{
"kind": "youtube#channelListResponse",
"etag": "AVEF8yG4pQMKfzZjMyy1rfVtFXA",
"pageInfo": {
"resultsPerPage": 1
},
"items": [
{
"kind": "youtube#channel",
"etag": "LpEHq7VFVl1gDbt97Gf4ObyFn0U",
"id": "UCBJycsmduvYEL83R_U4JriQ",
"statistics": {
"viewCount": "2084223845",
"commentCount": "0",
"subscriberCount": "12100000",
"hiddenSubscriberCount": false,
"videoCount": "1272"
}
}
]
}
if let item = statResponse.items.first {
completion(.success(item))
} else {
return completion(.failure(.decodingError))
}
这是我的代码:
import Foundation
struct StatResponse: Decodable {
let items: Items
}
struct Items: Decodable {
var statistics: Stats
}
struct Stats: Decodable {
let subscriberCount: Double
}
enum APIError: Error {
case wrongURL
case decodingError
case noData
}
extension URL {
static func url() -> URL? {
guard let url = URL(string: "https://www.googleapis.com/youtube/v3/channels?part=statistics&id=UCBJycsmduvYEL83R_U4JriQ&key=API_KEY") else {
return nil
}
return url
}
}
class StatService {
func getStats(completion: @escaping (Result<Items?,APIError>) -> Void) {
guard let url = URL.url() else {
return completion(.failure(.wrongURL))
}
URLSession.shared.dataTask(with: url) {
data, response, error in
guard let data = data, error == nil else {
return completion(.failure(.noData))
}
let statResponse = try? JSONDecoder().decode(StatResponse.self, from: data)
if let statResponse = statResponse {
completion(.success(statResponse.items))
} else {
return completion(.failure(.decodingError))
}
}.resume()
}
}
class StatsViewModel: ObservableObject {
@Published private var items: Items?
var subCount: Double {
guard let subscriberCount = items?.statistics.subscriberCount else {
return 0
}
return subscriberCount
}
func fetchStats() {
StatService().getStats {
result in switch result {
case .success(let Items):
DispatchQueue.main.async {
self.items = Items
}
case .failure(_ ):
print("fail")
}
}
}
}
<代码>导入基础
结构状态响应:可解码{
让项目:项目
}
结构项:可解码{
var统计:统计数据
}
结构统计:可解码{
让我们计数:双倍
}
枚举APIError:错误{
案例错误URL
案例解码错误
野田案例
}
扩展URL{
静态函数url()->url{
guard let url=url(字符串:https://www.googleapis.com/youtube/v3/channels?part=statistics&id=UCBJycsmduvYEL83R_U4JriQ&key=API_KEY)其他{
归零
}
返回url
}
}
类StatService{
func getStats(完成:@escaping(Result)->Void){
guard let url=url.url()else{
返回完成(.failure(.errorurl))
}
URLSession.shared.dataTask(带:url){
数据、响应、错误
保护let数据=数据,错误==nil else{
返回完成(.failure(.noData))
}
让statResponse=try?JSONDecoder().decode(statResponse.self,from:data)
如果让statResponse=statResponse{
完成(.success(statResponse.items))
}否则{
返回完成(.failure(.decodingError))
}
}1.简历()
}
}
类StatsViewModel:observeObject{
@已发布的专用var项目:项目?
var子计数:双{
guard let subscriberCount=项目?.statistics.subscriberCount else{
返回0
}
返回订阅计数
}
func fetchStats(){
StatService().getStats{
导致切换结果{
成功案例(让项目):
DispatchQueue.main.async{
self.items=项目
}
案例.失败(uu):
打印(“失败”)
}
}
}
}
我不知道出了什么问题,结果是0。我尝试了各种方法,比如
self.items?.statistics=items.statistics
而不是self.items=items
,因为我试图获取的数据是统计数据,但它也不起作用,并且出现了一个错误,错误是“可选类型'Stats'的值必须解压缩为'Stats'类型的值”我不知道该怎么办,我可以用Swift很容易地获取非嵌套对象,但我不能获取嵌套对象。一般来说,我是Swift和编程的初学者,如果这是一个愚蠢的问题,很抱歉占用您的时间。您的模型类有一个错误,items
属性是一个数组,您试图将其解码为一个对象,请将StatResponse
更改为:
struct StatResponse: Decodable {
let items: [Items]
}
在Stats
结构上将subscriberCount
更改为字符串:
struct Stats: Decodable {
let subscriberCount: String
}
然后您必须更改访问数据的方式,因为现在您正在访问一个数组。您可以使用数组的first
属性,该属性将返回和数组的第一个元素,如下所示:
{
"kind": "youtube#channelListResponse",
"etag": "AVEF8yG4pQMKfzZjMyy1rfVtFXA",
"pageInfo": {
"resultsPerPage": 1
},
"items": [
{
"kind": "youtube#channel",
"etag": "LpEHq7VFVl1gDbt97Gf4ObyFn0U",
"id": "UCBJycsmduvYEL83R_U4JriQ",
"statistics": {
"viewCount": "2084223845",
"commentCount": "0",
"subscriberCount": "12100000",
"hiddenSubscriberCount": false,
"videoCount": "1272"
}
}
]
}
if let item = statResponse.items.first {
completion(.success(item))
} else {
return completion(.failure(.decodingError))
}
您的模型类有错误,
items
属性是一个数组,您试图将其解码为对象,请将StatResponse
更改为:
struct StatResponse: Decodable {
let items: [Items]
}
在Stats
结构上将subscriberCount
更改为字符串:
struct Stats: Decodable {
let subscriberCount: String
}
然后您必须更改访问数据的方式,因为现在您正在访问一个数组。您可以使用数组的first
属性,该属性将返回和数组的第一个元素,如下所示:
{
"kind": "youtube#channelListResponse",
"etag": "AVEF8yG4pQMKfzZjMyy1rfVtFXA",
"pageInfo": {
"resultsPerPage": 1
},
"items": [
{
"kind": "youtube#channel",
"etag": "LpEHq7VFVl1gDbt97Gf4ObyFn0U",
"id": "UCBJycsmduvYEL83R_U4JriQ",
"statistics": {
"viewCount": "2084223845",
"commentCount": "0",
"subscriberCount": "12100000",
"hiddenSubscriberCount": false,
"videoCount": "1272"
}
}
]
}
if let item = statResponse.items.first {
completion(.success(item))
} else {
return completion(.failure(.decodingError))
}
不要使用
try?
对从外部资源下载的json进行解码时,使用try
和do/catch
并在catch中添加print(error)
。正如我的Joakim Danielson所说:try?
的意思是:“如果有一个错误是可以捕获的,并且可能提供了有关其失败原因的特定信息,这有助于调试和修复问题,我不关心它。”使用do
/catch
并打印错误。例如,它可能导致使用let items:[items]
而不是让items:items
…不要使用try?
当解码从外部资源下载的json时,使用try
和do/catch
并在catch中添加print(error)
。正如我的Joakim Danielson所说:try?
的意思是:如果有一个错误是可以捕获的,并且可能给出了关于它失败原因的具体信息,这有助于调试和修复问题,我不关心它。“使用do
/catch
并打印错误。例如,它可能导致使用let items:[items]
而不是让items:items
…您没有测试它,是吗?它会给出一个错误,说明“实例成员'items'不能用于'StatResponse'类型”;你想改用这种类型的值吗?“on如果让statResponse=statResponse.items。首先
你现在修复了它:)@Ckatalay,这是你的拼写错误,应该是statResponse.items,小写SY你没有测试它,是吗?它给出了一个错误提示”实例成员“items”不能用于类型“StatResponse”;你是想用这种类型的值吗?“如果让statResponse=statResponse.items。首先
你现在修复了它:)@Ckatalay,这是你的拼写错误,它应该是statResponse.items,带小写s