可编码:展平结构

Encodable: Flattening structures

假设我有一些具有 JSON 表示的类型:

{
  "count": 3,
  "name": "Pianos",
  "type": "instrument",
  "id": 1,
}

假设我想将其表示为一个 Swift 对象,如下所示:

struct SomeObject: Codable { // this is the object I'd like to represent
  let id: Int
  let details: SomeDetails
}

struct SomeDetails: Codable {
  let count: Int
  let name: String
  let type: String
}

解码此对象是轻而易举的事。但是在这种情况下编码如何工作,以便我可以编码成一个平面结构——我用来创建这个对象并在上面的 JSON 示例中共享的相同结构?

But how would encoding work in this instance?

有效:

struct SomeObject: Codable { 
    let id: Int
    let details: SomeDetails
}
struct SomeDetails: Codable {
    let count: Int
    let name: String
    let type: String
}
let someobject = SomeObject(id: 10, details: SomeDetails(count: 3, name: "ho", type: "hey"))
let json = try! JSONEncoder().encode(someobject)

如果硬要人为压扁,干脆自己写encode(to:),像这样:

struct SomeObject: Codable {
    let id: Int
    let details: SomeDetails
    enum Keys : String, CodingKey {
        case id
        case count
        case name
        case type
    }
    func encode(to enc: Encoder) throws {
        var con = try enc.container(keyedBy: Keys.self)
        try con.encode(id, forKey: .id)
        try con.encode(details.count, forKey: .count)
        try con.encode(details.name, forKey: .name)
        try con.encode(details.type, forKey: .type)
    }
}
struct SomeDetails: Codable {
    let count: Int
    let name: String
    let type: String
}
let someobject = SomeObject(id: 10, details: SomeDetails(count: 3, name: "ho", type: "hey"))
let json = try! JSONEncoder().encode(someobject)

如果以后有人读到这篇文章(嗨!),只需进入包含抽象值的构造类型即可。

struct SomeDetails: Codable {
    let count: Int
    let name: String
    let type: String
}


struct SomeObject: Codable {
    let id: Int
    let details: SomeDetails

    enum CodingKeys: String, CodingKey {
        case id
    }

    enum DetailKeys: String, CodingKey {
        case count, name, type
    }

    init(from decoder: Decoder) throws {
        let topLevelContainer = try decoder.container(keyedBy: CodingKeys.self)
        let detailContainer = try decoder.container(keyedBy: DetailKeys.self)

        id = try topLevelContainer.decode(Int.self, forKey: .id)
        details = SomeDetails(
            count: try detailContainer.decode(Int.self, forKey: .count),
            name: try detailContainer.decode(String.self, forKey: .name),
            type: try detailContainer.decode(String.self, forKey: .type))
    }

    func encode(to encoder: Encoder) throws {
        var topLevelContainer = encoder.container(keyedBy: CodingKeys.self)
        try topLevelContainer.encode(id, forKey: .id)

        var detailContainer = encoder.container(keyedBy: DetailKeys.self)
        try detailContainer.encode(details.count, forKey: .count)
        try detailContainer.encode(details.name, forKey: .name)
        try detailContainer.encode(details.type, forKey: .type)
    }
}

对之前答案的改进。无需手动编码或解码您可以转发初始化程序的其他对象。

struct SomeDetails: Codable {
  let count: Int
  let name: String
  let type: String
}

struct SomeObject: Codable {
  enum CodingKeys: String, CodingKey {
    case id
  }

  let id: Int
  let details: SomeDetails

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    id = try container.decode(Int.self, forKey: .id)
    details = try SomeDetails(from: decoder)
  }

  func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(id, forKey: .id)
    try details.encode(to: encoder)
  }
}

对于一些额外的糖分,您可以使用 Swift 5 秒动态成员查找,以便您可以很好地访问这些成员 object.name

@dynamicMemberLookup
struct SomeObject {
  ...

  subscript<T>(dynamicMember member: KeyPath<SomeDetails, T>) -> T {
    details[keyPath: member]
  }
}