Swift Codable:是否可以在其自身的“ encode(to :)”函数中向上一级编码结构?

时间:2018-11-06 08:08:04

标签: swift codable encodable

我正在使用一种协议来编码一致的结构:

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", 
   ...
}

编辑: 为了使问题更清楚,假设有可能,如何使此结构直接在其父级上通过键值编码?

extension SharedParameters: Encodable {

    func encode(to encoder: Encoder) throws {

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

    }
}

1 个答案:

答案 0 :(得分:0)

  

想知道是否可以将它们分组为另一种结构,并以某种方式修改编码以将其编码为顶层?

您不能更改当前的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)!)

输出

  

{   “ 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)

}
}