代码之家  ›  专栏  ›  技术社区  ›  Jan

合并可在Swift中编码

  •  0
  • Jan  · 技术社区  · 6 年前

    struct Session: Encodable {
        let sessionId: String
    }
    struct Person: Encodable {
        let name: String
        let age: Int
    }
    
    let person = Person(name: "Jan", age: 36)
    let session = Session(sessionId: "xyz")
    

    我需要将其编码为具有以下格式的json对象:

    {
      "name": "Jan",
      "age": 36,
      "sessionId": "xyz"
    }
    

    所有的钥匙 Session 合并到 Person

    Encodable SingleValueEncodingContainer 但它显然只能编码一个值

    struct RequestModel: Encodable {
        let session: Session
        let person: Person
    
        public func encode(to encoder: Encoder) throws {
            var container = encoder.singleValueContainer()
            try container.encode(person)
            // crash
            try container.encode(session)
        }
    }
    
    let person = Person(name: "Jan", age: 36)
    let session = Session(sessionId: "xyz")
    let requestModel =  RequestModel(session: session, person: person)
    
    let encoder = JSONEncoder()
    encoder.outputFormatting = .prettyPrinted
    
    let data = try encoder.encode(requestModel)
    let json = String(data: data, encoding: .utf8)!
    
    print(json)
    

    我无法更改json格式,因为它是一个固定的网络API。我可以要这个 sessionId 作为 但我想避免这种情况,因为它们是不相关的模型。

    另一种方法是 RequestModel 从中复制所有属性 会话 如下所示,但这不是很好,因为我的真实结构有更多的属性。

    struct RequestModel: Encodable {
        let sessionId: String
        let name: String
        let age: Int
    
        init(session: Session, person: Person) {
            sessionId = session.sessionId
            name = person.name
            age = person.age
        }
    }
    
    2 回复  |  直到 6 年前
        1
  •  3
  •   PGDev    6 年前

    使用 encoder.container(keyedBy: CodingKeys.self) 而不是 singleValueContainer()

    struct RequestModel: Encodable
    {
        let session: Session
        let person: Person
    
        enum CodingKeys: String, CodingKey {
            case sessionId, name, age
        }
    
        public func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)
            try container.encode(person.age, forKey: RequestModel.CodingKeys.age)
            try container.encode(person.name, forKey: RequestModel.CodingKeys.name)
            try container.encode(session.sessionId, forKey: RequestModel.CodingKeys.sessionId)
        }
    }
    

    输出:

    {
      "age" : 36,
      "name" : "Jan",
      "sessionId" : "xyz"
    }
    

    如果你还面临任何问题,请告诉我。

        2
  •  7
  •   marty-suzuki    6 年前

    呼叫 encode(to:) 每个可编码对象,而不是 singleValueContainer() 它可以将多个可编码对象连接到一个可编码对象中,而无需定义额外的 CodingKeys .

    struct RequestModel: Encodable {
        let session: Session
        let person: Person
    
        public func encode(to encoder: Encoder) throws {
            try session.encode(to: encoder)
            try person.encode(to: encoder)
        }
    }
    
        3
  •  0
  •   Ash    4 年前

    struct EncodableCombiner: Encodable {
        let subelements: [Encodable]
        func encode(to encoder: Encoder) throws {
            for element in subelements {
                try element.encode(to: encoder)
            }
        }
    }
    

    只需实例化一个可编码对象数组,并将生成的对象本身视为可编码对象。现在,使用此方法时需要记住几个重要注意事项:

    1. JSON中只能有一种类型的根对象,即单个值、数组或字典。所以当你实施 encode(to:) encoder.singleValueContainer .
    2. 要组合的每个对象都必须在同一种容器上操作,因此如果其中一个使用 unkeyedContainer() 他们也必须这样。同样,如果一个人使用 container(keyedBy:)
    3. 如果您使用的是键控容器,那么跨所有组合对象的两个变量不能共享同一个键名!否则,您会发现它们会互相覆盖,因为它们被解析到同一个字典中。

    另一种可以缓解这些问题,但不会产生相同的JSON结构的方法是:

    struct EncodableCombiner: Encodable {
        let elementA: MyEncodableA
        let elementB: MyEncodableB
        func encode(to encoder: Encoder) throws {
            var container = encoder.unkeyedContainer()
            try container.encode(elementA)
            try container.encode(elementB)
        }
    }
    

    现在,这稍微不那么方便,因为我们不能简单地提供一个符合可编码的对象数组;它需要确切地知道他们要叫什么 container.encode()

    struct EncodableCombiner: Encodable {
        let elementA: MyEncodableA
        let elementB: MyEncodableB
    }
    

    ... 这当然会产生一个dictionary根对象,其编码形式为 MyEncodableA elementA ,和 MyEncodableB 作为 elementB

    这完全取决于你想要什么样的结构。