Я пытаюсь отобразить следующую структуру SignUpRequest
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?
}
в JSON вот так:
{
"customer": {
"email": "[email protected]",
"firstname": "Mario",
"lastname": "Rossi",
"custom_attributes":[
{
"attribute_code":"consensopubb",
"value":"3"
},
{
"attribute_code":"consensopubb2",
"value":"5"
}
]
},
"password": "Password1!"
}
Я попытался создать собственную кодировку моей структуры.
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)
// ...
}
}
}
Моя проблема в том, что я не могу понять, как закодировать свойства privacy1 и privacy2 в массив JSON custom_attributes.
-- РЕДАКТИРОВАТЬ --
Свойства privacy1 и privacy2 — это Bool, потому что сопоставляют два флажка, которые пользователь может установить или нет. К сожалению, API нужен массив custom_attributes. consensopubb со значением "3", если privacy1 есть true. consensopubb2 со значением "5", если privacy2 есть true.
Как можно преобразовать Bool в значения массива custom_attributes? Почему вы используете Bool для этих двух свойств? Или это жестко закодированные значения, а Bool означает только то, следует ли их включать или нет?
@JoakimDanielson Я отредактировал свой вопрос, чтобы ответить на твой вопрос.





Возможное решение:
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
}
}
}
Тестирование с:
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)")
}
}
Теперь я создал собственную структуру AttributeCode, чтобы упростить задачу. Конечно, вы можете настроить все encode и избежать этой структуры (что довольно просто, поскольку это [String: String], но я чувствую, что иногда, если encode(to:) слишком сложно для пользователя (для последующей отладки, модификаций) или encode(to:) не Это не достаточно ясно с первого взгляда, гораздо лучше иметь явную дополнительную структуру, при необходимости вы можете сделать ее частной, и ее сможет видеть только SignUpRequest.Customer.
Я сделал собственную кодировку для Customer, но на самом деле это можно сделать с другими модификациями для SignUpRequest, если только для этого нужна эта специальная кодировка, и вы не хотите изменять кодировку Customer (если это не сделано через более высокий SignUpRequest), но с этим у вас должна быть вся идея.
Обновлено: если вам действительно не нужна структура AttributedCode, вы можете написать:
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)
Если бы это была [String: Any], это было бы сложнее, и я бы все равно предложил использовать структуру AttributedCode, поскольку ее легче читать на первый взгляд, но оба решения работают и действительны.
Спасибо Ларме. Но просто ради интереса, по вашему мнению, есть ли способ достичь моей цели без использования структуры поддержки AttributeCode ?
@Giorgio Вы можете изменить переменную массива на массив словарей, а затем использовать жестко запрограммированные значения в append. var attributes = [[String: String]]()а затем внутри предложения ifattributes.append(["attribute_code":"consensopubb", "value":"3"])
В предпоследнем абзаце я заявил, что это возможно, тем более, что это «легкий» [String: String] (это был бы [String: Any], который был бы более сложным.
Кастом
encode(to:)надо делать только вCustomer, нет? Быстрый способ — создатьstruct CustomAttributes: Encodableи создать его из значенийprivacy1иprivacy2. Я думаю, вы читали эти значения, чтобы создать их?