JSONDecoder не смог декодировать вложенные словари

Я использую JSONDecoder для декодирования из файла JSON, в котором есть вложенные словари. Он не может декодировать данные json в мою настроенную модель.

Это то, что я пробовал в своем коде.

  1. JSONDecoder выглядит так:
let netWorkManager = NetWorkManager(URL: url, httpMethodType: .GET)
        netWorkManager.callAPI { (data, status, error) in
            guard let data = data else {
                onFail(NetWorkError.otherError)
                return
            }

            switch status {
            case 200:
                do{
                    if let responseModel = try JSONDecoder().decode(ResonseModel?.self, from: data) {
                        onSuccess(responseModel)
                    }
                }catch {
                    onFail(NetWorkError.otherError)
                }
            default:
                onFail(NetWorkError.otherError)
            }
        }
  1. Модель выглядит так:
struct ResonseModel: Codable {
    let type : String
    let format: String
    let data: [String: Champion]

    struct Champion: Codable {
        let version: String
        let id: String
        let key: Int
        let name: String
        let title: String
        let blurb: String
    }
}

  1. Структура JSON выглядит так:
{
    "type": "champion",
    "format": "standAloneComplex",
    "version": "9.3.1",
    "data": {
        "Aatrox": {
            "version": "9.3.1",
            "id": "Aatrox",
            "key": "266",
            "name": "Aatrox",
            "title": "the Darkin Blade",
            "blurb": "Once honored defenders of Shurima against the Void, Aatrox and his brethren would eventually become an even greater threat to Runeterra, and were defeated only by cunning mortal sorcery. But after centuries of imprisonment, Aatrox was the first to find...",
            "info": {
                "attack": 8,
                "defense": 4,
                "magic": 3,
                "difficulty": 4
            },
            "tags": [
                "Fighter",
                "Tank"
            ],
            "partype": "Blood Well",

        },
        "Ahri": {
            "version": "9.3.1",
            "id": "Ahri",
            "key": "103",
            "name": "Ahri",
            "title": "the Nine-Tailed Fox",
            "blurb": "Innately connected to the latent power of Runeterra, Ahri is a vastaya who can reshape magic into orbs of raw energy. She revels in toying with her prey by manipulating their emotions before devouring their life essence. Despite her predatory nature...",
            "info": {
                "attack": 3,
                "defense": 4,
                "magic": 8,
                "difficulty": 5
            },

            "tags": [
                "Mage",
                "Assassin"
            ],
            "partype": "Mana",

        },
        ...

это ссылка на JSON, если вы хотите изучить его: http://ddragon.leagueoflegends.com/cdn/9.3.1/data/en_US/champion.json

Я хочу декодировать свойство «данные» как словарь, ключ которого - это имя чемпиона, а значение - чемпион. Но похоже, что jsonDecoder не распознает мою структуру модели. В конечном итоге он перехватывает ошибку.


person Steve    schedule 26.03.2019    source источник
comment
Вставьте свой JSON на этот сайт (app.quicktype.io), и он предоставит вам подходящие структуры для использования .   -  person Mohmmad S    schedule 26.03.2019
comment
Какая у вас ошибка? Предоставьте нам образец кода, демонстрирующий, что вы сделали на данный момент.   -  person Sergey Gamayunov    schedule 26.03.2019
comment
Я никогда не видел такого необязательного типа ResonseModel?.self. Что то, что для? Может быть, расшифровать? Это чушь, цель do catch - либо метод возвращает что-то необязательное в случае успеха, либо выдает ошибку. Белый или черный без оттенков серого. Внутри блока do пишите всегда let responseModel = try JSONDecoder().decode(ResonseModel.self, from: data) без if. И вы, вероятно, имеете в виду Res p onseModel ????   -  person vadian    schedule 26.03.2019
comment
PS: И catch всегда настоящая ошибка при декодировании JSON, а не при передаче бессмысленной фантастической ошибки. DecodingError очень информативны и сообщают вам, что не так и где.   -  person vadian    schedule 26.03.2019


Ответы (2)


«Ключ» параметра JSON не является целым числом.

Измените его на String, и он будет работать:

struct ResonseModel: Codable {
    let type : String
    let format: String
    let data: [String: Champion]

    struct Champion: Codable {
        let version: String
        let id: String
        let key: String
        let name: String
        let title: String
        let blurb: String
    }
}
person laka    schedule 26.03.2019
comment
Да! Это решило мою проблему. Спасибо. Я не заметил, что сделал такую ​​ошибку. - person Steve; 26.03.2019
comment
Или измените JSON, чтобы потерять кавычки вокруг этих значений. - person Caleb; 26.03.2019
comment
Я бы не предполагал, что изменение JSON всегда возможно. Часто вы просто потребляете конечную точку. Тогда вы должны извлечь из этого максимум пользы. - person laka; 17.05.2019

Вы можете переключиться на ручное декодирование Champion, чтобы очистить данные.

struct ResonseModel: Decodable {
    let type : String
    let format: String
    let data: [String: Champion]

    struct Champion: Decodable {
        let version: String
        let id: String
        let key: Int
        let name: String
        let title: String
        let blurb: String

        enum CodingKeys: String, CodingKey {
            case version, id, key, name, title, blurb
        }

        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.version = try container.decode(String.self, forKey: .version)
            self.id = try container.decode(String.self, forKey: .id)
            guard let key = Int(try container.decode(String.self, forKey: .key)) else {
                throw DecodingError.valueNotFound(Int.self,
                                                  .init(codingPath: decoder.codingPath,
                                                        debugDescription: "Bad value for id"))
            }
            self.key = key
            self.name = try container.decode(String.self, forKey: .name)
            self.title = try container.decode(String.self, forKey: .title)
            self.blurb = try container.decode(String.self, forKey: .blurb)
        }
    }
}

По сути, это код, который компилятор пишет для вас; он просто преобразует строку в int, потому что это то, что вы действительно хотели.

person Rob Napier    schedule 26.03.2019