Reputation: 2210
I'm trying to map the following SignUpRequest
struct
struct SignUpRequest: Encodable {
struct Customer: Encodable {
let email: String?
let firstname: String?
let lastname: String?
let privacy: Bool
let privacy2: Bool
}
let customer: Customer
let password: String?
}
in a JSON like this:
{
"customer": {
"email": "[email protected]",
"firstname": "Mario",
"lastname": "Rossi",
"custom_attributes":[
{
"attribute_code":"consensopubb",
"value":"3"
},
{
"attribute_code":"consensopubb2",
"value":"5"
}
]
},
"password": "Password1!"
}
I tried to create a custom encoding of my struct.
struct SignUpRequest: Encodable {
let customer: Customer
let password: String?
struct Customer: Encodable {
let email: String?
let firstname: String?
let lastname: String?
let privacy: Bool
let privacy2: Bool
enum CodingKeys: String, CodingKey {
case email
case firstname
case lastname
case attributes = "custom_attributes"
}
enum AttributeCodingKeys: String, CodingKey {
case code = "attribute_code"
case value
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(email, forKey: .email)
try container.encode(firstname, forKey: .firstname)
try container.encode(lastname, forKey: .lastname)
// ...
}
}
}
My problem is that I can't figure how to encode privacy1
and privacy2
properties into JSON custom_attributes
array.
-- EDIT --
privacy1
and privacy2
properties are Bool
because map two checkbox that user can check or not. Unfortunately API wants an array of custom_attributes
. consensopubb
with value "3"
if privacy1
is true
. consensopubb2
with value "5"
if privacy2
is true
.
Upvotes: 0
Views: 58
Reputation: 26036
A possible solution:
struct SignUpRequest: Encodable {
let customer: Customer
let password: String?
}
extension SignUpRequest {
struct Customer: Encodable {
let email: String?
let firstname: String?
let lastname: String?
let privacy1: Bool
let privacy2: Bool
enum CustomerCodingKeys: String, CodingKey {
case email
case firstname
case lastname
case attributes = "custom_attributes"
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CustomerCodingKeys.self)
var codes: [AttributeCode] = []
if privacy1 {
codes.append(AttributeCode(value: "3", code: "consensopubb"))
}
if privacy2 {
codes.append(AttributeCode(value: "5", code: "consensopubb2"))
}
try container.encode(codes, forKey: .attributes)
try container.encode(email, forKey: .email)
try container.encode(firstname, forKey: .firstname)
try container.encode(lastname, forKey: .lastname)
}
}
}
extension SignUpRequest.Customer {
struct AttributeCode: Encodable {
let value: String
let code: String
enum AttributeCodingKeys: String, CodingKey {
case code = "attribute_code"
case value
}
}
}
Testing with:
let requests: [SignUpRequest] = [SignUpRequest(customer: .init(email: "email1",
firstname: "f1",
lastname: "l1",
privacy1: true,
privacy2: true),
password: "p1"),
SignUpRequest(customer: .init(email: "email2",
firstname: "f2",
lastname: "l2",
privacy1: false,
privacy2: true),
password: "p2"),
SignUpRequest(customer: .init(email: "email3",
firstname: "f3",
lastname: "l3",
privacy1: true,
privacy2: false),
password: "p3"),
SignUpRequest(customer: .init(email: "email4",
firstname: "f4",
lastname: "l4",
privacy1: false,
privacy2: false),
password: "p4")]
requests.forEach { aRequest in
do {
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys] //For debug purposes
let data = try JSONEncoder().encode(aRequest)
let jsonString = String(data: data, encoding: .utf8)!
print(jsonString)
} catch {
print("Error: \(error)")
}
}
Now, I created a custom AttributeCode
struct to make thing easier. Of course, you could customize all the encode
and avoid that struct (which is pretty easy since it's a [String: String]
, but I feel that sometimes if the encode(to:)
if too difficult for the user (for later debug, modifications), or the encode(to:)
isn't clear enough at a quick glance, it a lot better to have explicit extra struct, you can make it private if needed and it can only be seen by SignUpRequest.Customer
.
I made the custom encode on Customer
, but indeed, it can be done with other modifications on SignUpRequest
if only that one needs that special encoding and you don't want to modify the encoding of Customer
(if it's not done through higher SignUpRequest
), but with this should you have the whole idea.
Edit: If you don't really want AttributedCode
struct, you can write:
var codes: [[String: String]] = []
if privacy1 {
codes.append(["value": "3", "attribute_code": "consensopubb"])
}
if privacy2 {
codes.append(["value": "5", "attribute_code": "consensopubb2"])
}
try container.encode(codes, forKey: .attributes)
It it was a [String: Any]
it'd be more complex, and I'd still suggest to have the AttributedCode
struct as it's easier to read at first glance, but both solution works and are valid.
Upvotes: 1