json - 使用 Swift Codable 解析 Reddit JSON

标签 json swift

我在解析 reddit 的 JSON 时遇到一个小问题。

简化的 reddit JSON 响应:

{
    "kind": "Listing",
    "data": {
        "after": "t3_c2wztu",
        "before": null,
        "children": [
            {
                "data": {
                    "all_awardings": [],
                    "approved_at_utc": null,
                    "approved_by": null
                },
                "kind": "t3"
            }
        ],
        "dist": 25,
        "modhash": ""
    }
}

我简化了 JSON,因为它可以达到 4000 行长,大部分是重复的子对象。

当我们查看“children”对象中的第二个“data”键时,我们就获得了所有需要的数据,例如图像、用户名等。

我在 Swift 中的结构如下:看到 JSON 是嵌套的,我相信我需要创建嵌套结构或多个结构来帮助深入了解 JSON。我这样说对吗?

struct Model: Decodable {
    let data : Children?
    enum CodingKeys: String, CodingKey {
        case data = "data"
    }
}

struct Children: Decodable {
    let data: [Child]?
    enum CodingKeys: String, CodingKey {
        case data = "data"
    }
}

//MARK - there is MORE than author and full name, we are just doing this for ease of parsing.
struct Child: Decodable {
    let author : String?
    let authorFullname : String?
    enum CodingKeys: String, CodingKey {
        case author = "author"
        case authorFullname = "author_fullname"
    }
     init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)

        author = try values.decodeIfPresent(String.self, forKey: .author)
        authorFullname = try values.decodeIfPresent(String.self, forKey: .authorFullname)
     }
}

现在这是我的 URLSession 调用:

class NetworkManager {

    static var shared: NetworkManager = { return NetworkManager() }()
    private init(){}

    public func getFrontPage(completion: @escaping (Model?) -> Void) {
        guard let url = URL(string: "https://www.reddit.com/r/all/.json") else { fatalError("Invalid URL") }
        URLSession.shared.dataTask(with: url) { data, response, err in
            if(err != nil) {
                completion(nil)
            }
            guard let httpResponse = response as? HTTPURLResponse else { fatalError("URL Response Error") }
            switch httpResponse.statusCode {
            case 200:
                if let data = data {
                    do {
                    let data = try JSONDecoder().decode(Model.self, from: data)
                    completion(data)
                    } catch {
                        print("ERROR DECODING JSON \(err.debugDescription)")
                    }
                }
                break
            default:
                completion(nil)
                break
            }
        }.resume()
    }

最后在 ViewDidLoad 中:

 var redditData = [Model]()

    override func viewDidLoad() {
        super.viewDidLoad()
        NetworkManager.shared.getFrontPage { (items) in
           // self.redditData = items?.data
            print(items)
            DispatchQueue.main.async {
                // update the UI
            }
        }
    }

这是我当前无法克服的错误:

self.redditData = items?.data 给我一个错误:

Cannot assign value of type 'Children?' to type '[Model]'

问题: 1.我的结构布局正确吗?不用担心实际数据,例如用户名等,我在这篇文章中缩短了它。

  • 我是否在网络管理器功能中正确“深入”了 JSON?

  • 如何修复 viewController 类中的错误?

  • 抱歉这个话题太长了!!

    最佳答案

    您的类型有点困惑。

    getFrontPage 的完成类型是 (Model?) -> Void

    那么让我们看看你在哪里使用它。

        NetworkManager.shared.getFrontPage { (items) in
            // items is Model? so...
            // items?.data is of type Children? 
            // BUT self.redditData is of type [Model]
            // self.redditData = items?.data
            print(items)
            DispatchQueue.main.async {
                // update the UI
            }
        }
    

    您显示的模型结构与示例 JSON 不匹配。这是一些可以工作的结构。我不熟悉 Reddit API,所以这些可能需要调整。

    struct Model : Decodable {
        let kind: String
        let data: ListingData
    }
    
    struct ListingData: Decodable {
        let after: String
        let before: String?
        let children: [Child]
        let dist: Int
        let modhash: String
    }
    
    struct ChildData : Decodable {
        let allAwardings: [String]
        let approvedAtUtc: String?
        let approvedBy: String?
    }
    struct Child: Decodable {
        let data: ChildData
        let kind: String?
    }
    

    如果您在 JSONDecoder 上使用 .convertFromSnakeCase,则可以避免所有 CodingKey 内容。只需将 getFrontPage 更改为如下解码:

        let decoder = try JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        let model = try decoder.decode(Model.self, from: data)
        completion(model)
    

    现在,只需确保 self.redditData 的类型与您感兴趣的模型上的字段匹配即可!

        var self.redditData = [Child]()
    
        NetworkManager.shared.getFrontPage { (model) in
            self.redditData = model?.data.children ?? []
            print(items)
            DispatchQueue.main.async {
                // update the UI
            }
        }
    

    希望这有帮助!

    关于json - 使用 Swift Codable 解析 Reddit JSON,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56710386/

    相关文章:

    ios - 在 iOS 内容拦截器中使用多个 JSON 列表

    javascript - 解析 JSON 以从父键获取子数组

    javascript - JS 过滤器/自动完成

    ios - 如何在不关闭父/第一个 View Controller 的情况下以模态方式呈现 View Controller

    swift - 在 swift spritekit 中更改定时器的时间间隔

    php - 为什么这个 API 在通过 php 代理调用时不再起作用?我该如何解决?

    ruby - 如何在 ruby​​ 中实现 argmax?

    ios - swift 中没有 MKPointAnnotation

    swift - 在 Swift 中不使用 case 语句从 Error 枚举获取消息

    swift - Swift 访问 URL 路径时删除部分字符串