Слияние в кодировке Swift - PullRequest
       10

Слияние в кодировке Swift

0 голосов
/ 25 октября 2018

У меня есть следующие структуры Swift

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 как свойство Person, но я бы хотел избежать этого, поскольку они не связаны между собой.

Другим способом может быть RequestModel, чтобы скопировать все свойства изSession и Person следующим образом, но это не очень хорошо, поскольку мои настоящие структуры имеют гораздо больше свойств.

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 ]

0 голосов
/ 26 декабря 2018

Вызовите 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)
    }
}
0 голосов
/ 25 октября 2018

Используйте 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"
}

Позвольте мнезнать, если у вас все еще есть какие-либо проблемы.

...