解码JSON中的嵌套对象

解码JSON中的嵌套对象,json,swift,Json,Swift,我正在尝试使用YouTube JSON API获取一些数据,JSON如下所示: { "kind": "youtube#channelListResponse", "etag": "AVEF8yG4pQMKfzZjMyy1rfVtFXA", "pageInfo": { "resultsPerPage": 1 }, "items":

我正在尝试使用YouTube JSON API获取一些数据,JSON如下所示:

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