使用Decodable + Realm Swift解码自定义JSON

时间:2018-11-22 15:52:29

标签: ios swift realm decodable

从服务器上,我返回了一个大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
}

它符合可解码协议,也是一个对象(领域实体)。 我的问题是:我可以通过某种方式解码personName中人的名字吗?类似于person.administration.name。
我希望最终的Realm Object成为扁平对象,并且几乎所有字段都是字符串。
我应该为Person / Company创建单独的结构而不是Realm Objects,还是在解码方法中将相应的值设置为“ personName”吗?

let personName: String = try container.decode((Person.Administration.name).self, forKey: .personName)

2 个答案:

答案 0 :(得分:1)

您可以简单地使用 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 typesCodable一起使用。这是因为API响应可能是意外的。而且,如果没有得到任何预期的键值对,则在创建对象时可能会得到nil

答案 1 :(得分:1)

最佳实践是将解析JSON的传输类型和代表存储对象的类型分开。

但是,如果要使用此组合类型,则应执行以下操作:

<Link>

为了某种类型的安全性,我将密钥分为三种不同的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) } } 类型,以防止意外混淆。