I have the following Swift structs
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")
that I need to encode to a json object that has this format:
{
"name": "Jan",
"age": 36,
"sessionId": "xyz"
}
where all keys of the Session
are merged into the keys of the Person
I thought about using a container struct with a custom Encodable
implementation where I use a SingleValueEncodingContainer
but it can obviously encode only one value
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)
I cannot change the json format as it's a fixed network API. I could have the sessionId
as property of the Person
but I'd like to avoid that as they are unrelated models.
Another way could be to have the RequestModel
copy all the properties from the Session
and Person
as follows but it's not very nice as my real structs have much more properties.
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
}
}
Call encode(to:)
of each encodable objects, instead of singleValueContainer()
.
It makes possible to join multi encodable objects into one encodable object without defining extra 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)
}
}
Use encoder.container(keyedBy: CodingKeys.self)
instead of singleValueContainer()
and add the key-value pairs separately, i.e.
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)
}
}
Output:
{
"age" : 36,
"name" : "Jan",
"sessionId" : "xyz"
}
Let me know in case you still face any issues.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With