Ожидается расшифровка словаряно нашел вместо этого массив с вложенными контейнерами - PullRequest
0 голосов
/ 27 октября 2018

Итак, я пытаюсь проанализировать этот JSON, используя протоколы Codable: https://randomuser.me/api/?results=100

Это в основном 100 случайных пользователей.

Вот мой инициализатор класса пользователя из декодера, который мне нужен, потому что пользователь является сущностью в базовой модели данных:

required convenience public init(from decoder: Decoder) throws {
        let managedObjectContext = CoreDataStack.sharedInstance.persistentContainer.viewContext
        guard let entity = NSEntityDescription.entity(forEntityName: "User", in: managedObjectContext) else {
                fatalError("Failed to decode User")
        }

        self.init(entity: entity, insertInto: managedObjectContext)

        let container = try decoder.container(keyedBy: CodingKeys.self)
        let results = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .results)
        let name = try results.nestedContainer(keyedBy: CodingKeys.self, forKey: .name)
        firstName = try name.decode(String.self, forKey: .firstName)
        lastName = try name.decode(String.self, forKey: .lastName)

        let location = try results.nestedContainer(keyedBy: CodingKeys.self, forKey: .location)
        let street = try location.decode(String.self, forKey: .street)
        let city = try location.decode(String.self, forKey: .city)
        let postcode = try location.decode(String.self, forKey: .postcode)
        address = street + ", " + city + ", " + postcode

        email = try results.decode(String.self, forKey: .email)

        let pictures = try results.nestedContainer(keyedBy: CodingKeys.self, forKey: .pictures)
        pictureURL = try pictures.decode(String.self, forKey: .pictureURL)
    }

Это дефектная строка:

let results = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .results)

Вот полная ошибка:

typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))

Я думаю, что это связано со структурой JSON, который представляет собой массив из 100 элементов под ключевыми «результатами», и я думаю, что проблема может заключаться в том, чтобы сделать их все вместе. Как мне справиться с этим?

Ответы [ 2 ]

0 голосов
/ 27 октября 2018

Ошибка ясна: значение для results является массивом, а nestedContainers ожидает словарь.

Для декодирования пользовательского массива вам необходима структура зонтика вне классов Core Data.

struct Root : Decodable {
   let results: [User]
}

При декодировании Root метод init в User вызывается для каждого элемента массива.

Чтобы использовать nestedContainers, необходимо разделить CodingKeys.

Это метод init без основных данных.postcode может быть String или Int

private enum CodingKeys: String, CodingKey { case name, location, email, picture }
private enum NameCodingKeys: String, CodingKey { case first, last }
private enum LocationCodingKeys: String, CodingKey { case street, city, postcode }
private enum PictureCodingKeys: String, CodingKey { case large, medium, thumbnail }

required convenience public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let nameContainer = try container.nestedContainer(keyedBy: NameCodingKeys.self, forKey: .name)
    let firstName = try nameContainer.decode(String.self, forKey: .first)
    let lastName = try nameContainer.decode(String.self, forKey: .last)

    let locationContainer = try container.nestedContainer(keyedBy: LocationCodingKeys.self, forKey: .location)
    let street = try locationContainer.decode(String.self, forKey: .street)
    let city = try locationContainer.decode(String.self, forKey: .city)
    let postcode : String
    do {
        let postcodeInt = try locationContainer.decode(Int.self, forKey: .postcode)
        postcode = String(postcodeInt)
    } catch DecodingError.typeMismatch {
        postcode = try locationContainer.decode(String.self, forKey: .postcode)
    }
    let address = street + ", " + city + ", " + postcode

    let email = try container.decode(String.self, forKey: .email)

    let pictureContainer = try container.nestedContainer(keyedBy: PictureCodingKeys.self, forKey: .picture)
    let pictureURL = try pictureContainer.decode(URL.self, forKey: .large)
}
0 голосов
/ 27 октября 2018

Это очень упрощенная версия, но она правильно обрабатывает ваши данные Json

struct Result : Codable {
    let results: [User]
}
struct User: Codable {
    let gender: String
    let name: Name
}
struct Name: Codable {
    let title: String
    let first: String
    let last: String
}

let decoder = JSONDecoder()
let data = jsonString.data(using: .utf8) //Replace with url download

do {
    let json = try decoder.decode(Result.self, from: data!)
} catch {
    print(error)
}
...