Swift Codable: возможно ли закодировать структуру на один уровень выше в пределах своей собственной функции «encode (to :)»? - PullRequest
0 голосов
/ 06 ноября 2018

Я использую протокол для кодирования соответствующих структур:

protocol RequestParameters: Encodable {

}

extension RequestParameters {

    func dataEncoding() -> Data? {
        guard let data = try? JSONEncoder().encode(self) else { return nil }
        return data
    }
}

Это прекрасно работает для кодирования таких структур:

struct StoreRequest: RequestParameters {

    var storeCode : String

    var storeNumber : String    
}

Однако иногда мои запросы требуют некоторых «общих» параметров:

struct SpecialStoreRequest: RequestParameters {

    var storeCode : String

    var storeNumber : String  

    // Shared Parameters that appear in 70% of my requests
    var sharedParam1 : String?
    var sharedParam2 : String?
    var sharedParam3 : String?
    var sharedParam4 : String?
    var sharedParam5 : String?
}

Я мог бы просто записать эти общие параметры в каждую из моих структур запросов, которые в них нуждаются, но Мне было интересно, можно ли сгруппировать их в другую структуру и каким-то образом изменить кодировку, чтобы вместо этого кодировать их на верхнем уровне

Я думаю о чем-то похожем на это:

struct SharedParameters {

    // Shared Parameters that appear in 70% of my requests
    var sharedParam1: String?
    var sharedParam2: String?
    var sharedParam3: String?
    var sharedParam4: String?
    var sharedParam5: String?

    enum CodingKeys: String, CodingKey {
        case sharedParam1
        case sharedParam2
        case sharedParam3
        case sharedParam4
        case sharedParam5
    }
}

struct SpecialStoreRequest: RequestParameters {

    var storeCode : String

    var storeNumber : String  

    var sharedParams : SharedParameters?
}

Проблема с этой последней структурой состоит в том, что результирующая кодировка НЕ совпадает с первой, потому что мои общие параметры будут закодированы INSIDE ключом "sharedParams":

{
   "storeCode" : "ABC",
   "storeNumber" : "123456",
   "sharedParams" : {"sharedParam1" : "A","sharedParam2" : "B", ...}
}

Но мне нужно, чтобы они были закодированы вместе с другими моими существующими параметрами (в данном случае storeCode & storeNumber).

{
   "storeCode" : "ABC",
   "storeNumber" : "123456",
   "sharedParam1" : "A",
   "sharedParam2" : "B", 
   ...
}

EDIT: Чтобы прояснить вопрос, предполагая, что это возможно, что нужно сделать, чтобы эта структура была закодирована ключом-значением непосредственно в ее родительском элементе?

extension SharedParameters: Encodable {

    func encode(to encoder: Encoder) throws {

        // What goes here? (Is it even possible?)

    }
}

1 Ответ

0 голосов
/ 06 ноября 2018

Интересно, можно ли сгруппировать их в другую структуру и как-то изменить кодировку, чтобы вместо этого кодировать их на верхнем уровне?

Вы не можете изменить текущий Encoder и его поведение, но,

Этого можно добиться с помощью настройки функций Encode,

создает два контейнера и использует общие параметры CodingKeys для кодирования

параметры внутри вашей sharedParameters переменной.

Соблюдайте код ниже.

    struct Others: Codable {
    var sharedParam1: String
    var sharedParam2: String

    enum CodingKeys: String, CodingKey {
        case sharedParam1
        case sharedParam2
    }
}



  struct MyCustomReq: Codable {
    var p1: String

    var p2: String

    var shared: Others

    enum CodingKeys: String, CodingKey {
        case p1
        case p2
        case shared
    }
}

    extension MyCustomReq {
        func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)
            try container.encode(p1, forKey: .p1)
            try container.encode(p2, forKey: .p2)
            //Others = sharedParams container. with its CodingKeys
            var container2 = encoder.container(keyedBy: Others.CodingKeys.self)
            try container2.encode(shared.sharedParam1, forKey: .sharedParam1 )
            try container2.encode(shared.sharedParam1, forKey: .sharedParam2)
        }
    }

Тест использования

var oth = Others(sharedParam1: "Shared1", sharedParam2: "Shared2")
var object = MyCustomReq.init(p1: "P1", p2: "P2", shared: oth)

let encoder = JSONEncoder()
let data = try encoder.encode(object)

print(String(data: data, encoding: .utf8)!)

OUTPUT

{ "P2": "P2",

"sharedParam1": "Shared1",

"p1": "P1",

"sharedParam2": "Shared1"

}

Теперь давайте перейдем к следующему шагу,

Создайте класс и настройте Encoder общего ресурса там и просто вызовите его функцию.

Обратите внимание на конечный результат.

final class MyParamsEncoded: Codable {
var sharedParams: Others
init (sharedParam: Others) {
    self.sharedParams = sharedParam
}
func encode(to encoder: Encoder) throws {
    var container2 = encoder.container(keyedBy: Others.CodingKeys.self)
    try container2.encode(sharedParams.sharedParam1, forKey: .sharedParam1 )
    try container2.encode(sharedParams.sharedParam1, forKey: .sharedParam2)
}
}

Теперь, после добавления этого класса, вы можете просто использовать его следующим образом, И это даст вам тот же результат.

extension MyCustomReq {
func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(p1, forKey: .p1)
    try container.encode(p2, forKey: .p2)
    //Using the class wrapping, final Result of using 
    var cont = try MyParamsEncoded(sharedParam: shared).encode(to: encoder)

}
}
...