因此,我尝试使用可编码协议解析此JSON:https://randomuser.me/api/?results=100
这基本上是100个随机用户。
这是解码器中的User类初始值设定项,我需要它,因为User是核心数据模型中的一个实体:
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的结构,即键"results"下的100个元素的数组,我认为问题可能在于将它们全部放在一起。我该如何处理?
错误很明显:results
的值是一个数组,nestedContainers
需要一个字典。
要解码用户数组,您需要在核心数据类之外有一个伞形结构。
struct Root : Decodable {
let results: [User]
}
在解码Root
时,对每个数组项调用User
中的init
方法。
要使用nestedContainers
,您必须分离编码键。
这是没有核心数据的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)
}
这是一个非常简化的版本,但它可以正确处理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)
}