使用 Codable 解析 JSON 响应会在 swift 中出现错误

Posted

技术标签:

【中文标题】使用 Codable 解析 JSON 响应会在 swift 中出现错误【英文标题】:Parsing JSON response using Codable gives error in swift 【发布时间】:2019-02-09 10:21:56 【问题描述】:

我正在尝试使用 Codable 解析 JSON 响应,但它给了我错误。 我试图从下面的堆栈溢出链接中引用,但没有奏效。 how do I parse Any in dictionary using swift

下面是我的代码,不知道哪里错了。

> enum JSONError: String,Error 
>         case NoData = "ERROR: no data"
>         case ConversionFailed = "ERROR: conversion from JSON failed"
>     

struct Owner : Decodable 
    let full_name : String
    let html_url:String
    let follower:follower


struct follower : Decodable 
    let followers_url : String



func jsonParser() 
        let urlPath = "https://api.github.com/search/repositories?q=language:ruby&sort=stars&order=desc"
        guard let endpoint = NSURL(string: urlPath) else 
            print("Error creating endpoint")
            return
        

        let request = NSMutableURLRequest(url:endpoint as URL)
        URLSession.shared.dataTask(with: request as URLRequest)  (data, response, error) in
            do 
                guard let data = data else 
                    throw JSONError.NoData
                

                let jsonResponse = try JSONSerialization.jsonObject(with:
                    data)

                let entries = try! JSONDecoder().decode([Owner].self, from: jsonResponse as! Data)
                print(jsonResponse)
             catch let error as JSONError 
                print(error.rawValue)
             catch let error as NSError 
                print(error.debugDescription)
            
            .resume()
    

我需要从此响应中获取 3 条信息 - 全名、html url 和关注者。 网页接口链接

https://api.github.com/search/repositories?q=language:ruby

请最新查看代码。

以下是错误信息:

'__NSDictionaryI' (0x102965a98) 到 'NSData' (0x102964580)。 2019-02-09 16:17:42.062971+0530 PhotoViewwer[13342:259997] 无法投射值 类型为“__NSDictionaryI”(0x102965a98)到“NSData”(0x102964580)。

谢谢

【问题讨论】:

您遇到了哪个错误? @vadian - 错误消息 - '__NSDictionaryI' (0x102965a98) 到 'NSData' (0x102964580)。 2019-02-09 16:17:42.062971+0530 PhotoViewwer[13342:259997] 无法将“__NSDictionaryI”(0x102965a98)类型的值转换为“NSData”(0x102964580)。 代码中有很多很多错误,请看我的回答。例如,如果您要使用DecodableJSONSerialization 行是无意义的。顺便说一句,这会导致错误。 @vadian - 通过你的代码,这样我就会知道我的错误。谢谢瓦迪安。 【参考方案1】:

请学习阅读 JSON。这很容易。集合类型只有两种,数组([])和字典(

你的结构错了。

在 JSON 的根字典中有一个字典数组,用于键 items。 在每个字典中都有键 full_nameowner这里Owner 结构的位置)。 键 follower 的字典存在。

这些结构正确地表示 JSON

struct Response : Decodable 
    let items : [Item]


struct Item : Decodable 
    let fullName : String
    let owner : Owner


struct Owner : Decodable 
    let htmlUrl : URL // URL strings can be decoded directly into URL
    let followersUrl : URL

为您的函数添加一个完成处理程序并使用枚举作为结果类型。 failure 案例返回所有 real 错误。 URLRequest 是多余的。只需传递 URL。 JSONSerialization 行毫无意义。

convertFromSnakeCase 策略将 snake_cased 键转换为 camelCased 结构成员

enum Result 
    case success(Response), failure(Error)


func jsonParser(completion: @escaping (Result) -> Void) 
    let endpoint = URL(string:"https://api.github.com/search/repositories?q=language:ruby&sort=stars&order=desc")!
    URLSession.shared.dataTask(with: endpoint)  (data, response, error) in
        if let error = error  completion(.failure(error)); return 
        do 
            let decoder = JSONDecoder()
            decoder.keyDecodingStrategy = .convertFromSnakeCase
            let entries = try decoder.decode(Response.self, from: data!)
            completion(.success(entries))
         catch 
            completion(.failure(error))
        
    .resume()

然后叫它

jsonParser  result in
    switch result 
    case .success(let entries) : print(entries)
    case .failure(let error) : print(error)
    

如果有本地等价类,则基本上从不使用NS... 类,这里URL 用于NSURLURLRequest 用于NS(Mutable)URLRequest

编辑:

在 Swift 5 中,使用原生 Result 类型的语法变得更加方便。它能够转换投掷表达式

罢工>

enum Result 
    case success(Response), failure(Error)
  

func jsonParser(completion: @escaping (Result<Response,Error>) -> Void) 
    let endpoint = URL(string:"https://api.github.com/search/repositories?q=language:ruby&sort=stars&order=desc")!
    URLSession.shared.dataTask(with: endpoint)  (data, response, error) in
        if let error = error  completion(.failure(error)); return 

        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        completion(Result try decoder.decode(Response.self, from: data!) )

    .resume()

【讨论】:

以上运行良好,但在一种情况下它失败了。如果全名为 null 它失败,我该如何处理这种情况 - 我希望全名是 nil 而不是在解析 json 时失败。 声明结构成员为可选:let fullName : String?【参考方案2】:

你需要

func jsonParser() 
    let urlPath = "https://api.github.com/search/repositories?q=language:ruby&sort=stars&order=desc"
    guard let endpoint = NSURL(string: urlPath) else 
        print("Error creating endpoint")
        return
    

    let request = NSMutableURLRequest(url:endpoint as URL)
    URLSession.shared.dataTask(with: request as URLRequest)  (data, response, error) in
        do 

            let dec = JSONDecoder()
            dec.keyDecodingStrategy = .convertFromSnakeCase
            let entries = try dec.decode(Root.self, from:data!)
            print(entries)
         catch 
            print(error)
        
        .resume()


struct Root : Decodable 
    let items:[Item]

struct Owner: Codable 
    let login: String
    let id: Int
    let nodeId: String
    let avatarUrl: String
    let gravatarId: String
    let url, htmlUrl, followersUrl: String
    let followingUrl, gistsUrl, starredUrl: String
    let subscriptionsUrl, organizationsUrl, reposUrl: String
    let eventsUrl: String


struct Item: Codable 
    let fullName : String
    let htmlUrl:String
    let owner: Owner

您不应该在此处将响应转换为数据

from: jsonResponse as! Data)

因为它会崩溃

【讨论】:

以上是关于使用 Codable 解析 JSON 响应会在 swift 中出现错误的主要内容,如果未能解决你的问题,请参考以下文章

使用 Codable 解析 JSON 数据

Swift Codable:如果 API 将来可能向响应 JSON 添加更多属性,则包括枚举编码键

使用 Codable 解析多级 JSON

使用 Alamofire/Codable 解析 JSON 行

如何使用 Codable 解码具有更改键的 json 响应?

使用 Codable 解析嵌套 JSON 数据问题