Разбор Reddit JSON с помощью Swift Codable - PullRequest
1 голос
/ 21 июня 2019

У меня небольшая проблема с анализом JSON из reddit.

Упрощенный ответ JSON reddit:

{
    "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 строк, в основном повторяющихся дочерних объектов.

Когда мы смотрим на второй ключ «data» внутри объекта «children», тогда мы получаем все необходимые данные, такие как изображения, имена пользователей и т. Д.

Мои структуры в 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
            }
        }
    }

Вот моя текущая ошибка Iне может пройти:

self.redditData = items? .data выдает ошибку:

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

Вопросы: 1. Правильно ли задан мой структурный макет?Не беспокойтесь о реальных данных, таких как имена пользователей и все остальное, я сократил их для этого поста.

правильно ли я "сверляю" через JSON в функции сетевого менеджера?

Как исправить ошибку в классе viewController?

Извините за такую ​​длинную тему !!

1 Ответ

1 голос
/ 22 июня 2019

Вы только что немного перепутали свои типы.

Тип вашего завершения для 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. Вот некоторые структуры, которые будут работать. Я не знаком с API Reddit, поэтому их, возможно, придется настроить.

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

Вы можете избежать всех вещей CodingKey, если используете .convertFromSnakeCase на JSONDecoder. Просто измените 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
        }
    }

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...