使用Swift Codable解析Reddit JSON

使用Swift Codable解析Reddit JSON,json,swift,Json,Swift,我在从reddit解析JSON时遇到了一个小问题 简化的reddit JSON响应: { "kind": "Listing", "data": { "after": "t3_c2wztu", "before": null, "children": [ { "data": { "all_awardings": [],

我在从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
    的完成类型是
    (型号?->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不匹配。这里有一些结构可以工作。我不熟悉RedditAPI,因此可能需要对其进行调整

    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
            }
        }
    

    希望这有帮助

    我知道我的孩子数据有点不可靠,在我需要它的地方是正确的,但我没有修复它在这里的外观…但是,这比我拥有的要干净得多!非常感谢!但它解决了你的主要问题吗?self.redditData?????completion(data)的赋值引发以下问题:无法将“data”类型的值转换为预期的参数类型“Model?”,让data=try JSONDecoder().decode(Model.self,from:data)JSONDecoder().keyDecodingStrategy=.convertFromSnakeCase completion(data)。这得到了我的JSON响应!哎呀!我明白问题所在。我会修好的。