我正在开发的iOS应用程序中尝试使用的API是JSON API。我使用了一个库来解析服务器检索到的数据。现在,我正尝试使用Swift引入的新的Codable协议自行解析JSON。
我成功地解析了data
和attributes
部分,但我面临的困难在于included
部分。
首先,我创建了这个类:
class UserCodable: Codable {
var data: UserCodableData?
var relationships: UserRelationships?
enum CodingKeys: String, CodingKey {
case data
case relationships = "included"
}
}
以便存储为用户对象检索的数据。
与这个类一起,我创建了这两个结构
struct UserCodableData: Codable {
var id: String?
var type: String?
var attributes: UserAttributes?
}
struct UserAttributes: Codable {
var id: String?
var type: String?
var firstName: String?
var lastName: String?
var email: String?
var officePhone: String?
var mobilePhone: String?
var timeZone: String?
var active: Int?
var middleName: String?
var `extension`: String?
var homePhone: String?
var avatar: String?
enum CodingKeys: String, CodingKey {
case id
case type
case firstName = "firstname"
case lastName = "lastname"
case email
case officePhone = "office_phone"
case mobilePhone = "mobile_phone"
case timeZone = "time_zone"
case active
case middleName = "middlename"
case `extension`
case homePhone = "home_phone"
case avatar
}
}
以便适当地存储数据和属性。
最后,我创建了关于关系的结构(包括):
struct UserRelationships: Codable {
var role: RoleCodable?
}
RoleCotable类遵循相同的模式。
检索到的包含密钥的数据如下:
"data": {
},
"included": [
{
"id": "10",
"type": "roles",
"attributes": {
"title": "Role"
}
}
],
问题是included
部分包含一个JSON对象数组。如何解码和初始化UserRelationships
类中的对象——在这种情况下是RoleCodable
类型的role
?
这并不优雅,但我就是这样做的,它涉及到UserDecodable
的手动init(from decoder: Decoder)
函数。基本上,我在"包含"部分读了两遍:第一次是收集类型信息,第二次是构建与正确类型的关系。
我使用关键字.included
表示"包含",而不是.relationships
,因为"关系"作为JSON API响应中的另一个部分出现,可能会引起混淆。
以下是类型信息的容器,您可以对任何包含的关系重复使用:
struct ResourceIdentifierObject: Codable {
let type: String
let id: String
enum CodingKeys: String, CodingKey {
case type
case id
}
}
这是第一次阅读:
let resourceIds = try data.decode([ResourceIdentifierObject].self,
forKey: .included)
第二次,我们遍历"included"JSON数组,同时检查ResourceIdentifierObject
中同一索引处的类型,并手动构建每个关系(我添加了一个注释关系来演示不同的关系):
var included = try data.nestedUnkeyedContainer(forKey: .included)
while !included.isAtEnd {
let resourceId = resourceIds[included.currentIndex]
switch resourceId.type {
case "roles":
role = try included.decode(RoleCodable.self)
case "comments":
comments.append(try included.decode(CommentCodable.self))
default:
print("unexpected type")
}
}