使用 Decodable + Realm Swift 解码自定义 JSON
Decode custom JSON with Decodable + Realm Swift
我从服务器返回了一个很大的 JSON,看起来像这样:
{
"id": "123",
"status": "ok",
"person": {
"administration": {
"name": "John"
}
},
"company": {
"name": "Test"
}
}
我有一个结构:
struct Info: Decodable, Object {
let id: String
let status: String
let personName: String
let companyName: String
}
符合Decodable协议,也是一个Object(Realm实体)。
我的问题是:我能以某种方式解码 personName 中的人名吗?类似于 person.administration.name.
我希望最终的 Realm 对象是一个平面对象,并且大部分字段都是字符串。
我是否应该为 Person/Company 创建单独的结构而不是领域对象,并在解码方法中将相应的值设置为 "personName"?
let personName: String = try container.decode((Person.Administration.name).self, forKey: .personName)
您可以简单地使用 containers
来解码带有 Decodable
的嵌套数据,即
struct Info: Decodable {
let id: String
let status: String
let personName: String
let companyName: String
enum CodingKeys: String, CodingKey {
case id, status
case person, administration
case company
case name
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(String.self, forKey: .id)
status = try values.decode(String.self, forKey: .status)
//Decoding personName
let person = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .person)
let administration = try person.nestedContainer(keyedBy: CodingKeys.self, forKey: .administration)
personName = try administration.decode(String.self, forKey: .name)
//Decoding companyName
let company = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .company)
companyName = try company.decode(String.self, forKey: .name)
}
}
示例:
我已经解码了你上面提供的JSON
,即
if let data = json.data(using: .utf8) {
let info = try? JSONDecoder().decode(Info.self, from: data)
print(info)
}
它给出的输出是:
(id: "123", status: "ok", personName: "John", companyName: "Test")
您可以根据自己的意愿将所有不同级别的 CodingKeys
分开。为简单起见,我将它们保持在同一水平。
建议: 尝试使用 optional types
和 Codable
。这是因为 API
响应可能出乎意料。如果您没有得到任何预期的键值对,您最终可能会在创建对象时得到 nil
。
最佳做法是将要解析 JSON 的传输类型与表示存储中对象的类型分开。
但是如果你想使用这种组合类型,你应该这样做:
struct Info: Decodable {
let id: String
let status: String
let personName: String
let companyName: String
// JSON root keys
private enum RootKeys: String, CodingKey {
case id, status, person, company
}
// Keys for "person" nested "object"
private enum PersonKeys: String, CodingKey {
case administration
}
// Keys for "administration" and "company"
private enum NamedKeys: String, CodingKey {
case name
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RootKeys.self)
self.id = try container.decode(String.self, forKey: .id)
self.status = try container.decode(String.self, forKey: .status)
let personContainer = try container.nestedContainer(keyedBy: PersonKeys.self, forKey: .person)
let administrationContainer = try personContainer.nestedContainer(keyedBy: NamedKeys.self, forKey: .administration)
self.personName = try administrationContainer.decode(String.self, forKey: .name)
let companyContainer = try container.nestedContainer(keyedBy: NamedKeys.self, forKey: .company)
self.companyName = try companyContainer.decode(String.self, forKey: .name)
}
}
我将密钥分为三种不同的 CodingKey
类型以确保类型安全,并防止意外混淆。
我从服务器返回了一个很大的 JSON,看起来像这样:
{
"id": "123",
"status": "ok",
"person": {
"administration": {
"name": "John"
}
},
"company": {
"name": "Test"
}
}
我有一个结构:
struct Info: Decodable, Object {
let id: String
let status: String
let personName: String
let companyName: String
}
符合Decodable协议,也是一个Object(Realm实体)。
我的问题是:我能以某种方式解码 personName 中的人名吗?类似于 person.administration.name.
我希望最终的 Realm 对象是一个平面对象,并且大部分字段都是字符串。
我是否应该为 Person/Company 创建单独的结构而不是领域对象,并在解码方法中将相应的值设置为 "personName"?
let personName: String = try container.decode((Person.Administration.name).self, forKey: .personName)
您可以简单地使用 containers
来解码带有 Decodable
的嵌套数据,即
struct Info: Decodable {
let id: String
let status: String
let personName: String
let companyName: String
enum CodingKeys: String, CodingKey {
case id, status
case person, administration
case company
case name
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(String.self, forKey: .id)
status = try values.decode(String.self, forKey: .status)
//Decoding personName
let person = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .person)
let administration = try person.nestedContainer(keyedBy: CodingKeys.self, forKey: .administration)
personName = try administration.decode(String.self, forKey: .name)
//Decoding companyName
let company = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .company)
companyName = try company.decode(String.self, forKey: .name)
}
}
示例:
我已经解码了你上面提供的JSON
,即
if let data = json.data(using: .utf8) {
let info = try? JSONDecoder().decode(Info.self, from: data)
print(info)
}
它给出的输出是:
(id: "123", status: "ok", personName: "John", companyName: "Test")
您可以根据自己的意愿将所有不同级别的 CodingKeys
分开。为简单起见,我将它们保持在同一水平。
建议: 尝试使用 optional types
和 Codable
。这是因为 API
响应可能出乎意料。如果您没有得到任何预期的键值对,您最终可能会在创建对象时得到 nil
。
最佳做法是将要解析 JSON 的传输类型与表示存储中对象的类型分开。
但是如果你想使用这种组合类型,你应该这样做:
struct Info: Decodable {
let id: String
let status: String
let personName: String
let companyName: String
// JSON root keys
private enum RootKeys: String, CodingKey {
case id, status, person, company
}
// Keys for "person" nested "object"
private enum PersonKeys: String, CodingKey {
case administration
}
// Keys for "administration" and "company"
private enum NamedKeys: String, CodingKey {
case name
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RootKeys.self)
self.id = try container.decode(String.self, forKey: .id)
self.status = try container.decode(String.self, forKey: .status)
let personContainer = try container.nestedContainer(keyedBy: PersonKeys.self, forKey: .person)
let administrationContainer = try personContainer.nestedContainer(keyedBy: NamedKeys.self, forKey: .administration)
self.personName = try administrationContainer.decode(String.self, forKey: .name)
let companyContainer = try container.nestedContainer(keyedBy: NamedKeys.self, forKey: .company)
self.companyName = try companyContainer.decode(String.self, forKey: .name)
}
}
我将密钥分为三种不同的 CodingKey
类型以确保类型安全,并防止意外混淆。