Swift 如何通过指定编码键的子集对可编码类型进行编码?

Swift 如何通过指定编码键的子集对可编码类型进行编码?,swift,encoding,Swift,Encoding,我为一些类型提供了CodingKeys,并根据需要为其数据成员提供自定义名称。我希望在将数据发送到服务器之前,根据不同呼叫站点的要求,仅对类型的编码键的子集进行编码。所需的编码从调用站点更改为调用站点,因此没有默认的实现需要考虑。 示例类型(用于演示): 创建实例: var user = User( id: 1, name: "john", account: Account(name: "checking", balance: 10_000));

我为一些类型提供了
CodingKeys
,并根据需要为其数据成员提供自定义名称。我希望在将数据发送到服务器之前,根据不同呼叫站点的要求,仅对类型的
编码键的子集进行编码。所需的编码从调用站点更改为调用站点,因此没有默认的实现需要考虑。
示例类型(用于演示):

创建实例:

var user = User(
    id: 1, name: "john", account: Account(name: "checking", balance: 10_000));
使用
jsonecoder
可以正常工作;它产生以下结果:

{
  "db_id" : 1,
  "db_name" : "john",
  "account" : {
    "balance" : 10000,
    "name" : "checking"
  }
}
我想对
用户
类型的一个子集进行编码,以便将该数据发送回服务器,以便更新特定的数据字段,而不是更新类型的整个属性集。模拟使用示例:

user.account.set(balance: 15_000);
let jsonEncoding = JSONEncodeSubset.of(
    user, // instance to encode
    keys: [User.CodingKeys.id, User.CodingKeys.account] // data to include
);
由此产生的JSON如下所示:

{
  "db_id" : 1,
  "account" : {
    "balance" : 15000,
    "name" : "checking"
  }
}
在服务器端,我们现在拥有执行所需更新所需的确切数据

另一个示例:有人为我们的用户输入了错误的名称,因此另一个更新请求如下所示:

user.name = "jon"; // assume the model was modified to make this mutable
let jsonEncoding = JSONEncodeSubset.of(
    user, // instance to encode
    keys: [User.CodingKeys.id, User.CodingKeys.name] // only encode id & name
);
let encoder = JSONEncoder().withEncodeSubset(keysToEncode: [User.CodingKeys.id, .account])
let encoded = try encoder.encode(user)

print(String(data: encoded, encoding: .utf8)!)
// prints: {"db_id":1,"account":{"name":"checking","balance":15000}}
预期的JSON编码结果:

{
  "db_id" : 1,
  "name" : "jon"
}
请注意,我们排除了不属于更新的信息(用户帐户)。目标是优化编码,使其仅包含与特定请求相关的数据

考虑到我有大量的对象要更新,不同的调用站点更新不同的东西,我希望有一种简洁的方法来执行编码任务

  • 对类型数据成员的子集进行编码可以提高编码性能/内存占用
  • 向服务器发送较大数量的较小编码对象会更有效

  • Swift是否为编码类型的
    编码键的子集提供任何此类支持?

    此解决方案要求您为所有属性编写自定义
    编码(to:)
    ,但一旦完成,就应该易于使用

    其思想是让
    只对给定数组中存在的那些属性/键进行编码(to:)
    编码。因此,给出上面的示例,函数如下所示

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        for key in selectedCodingKeys {
            switch key {
            case .id:
                try container.encode(id, forKey: .id)
            case .account:
                try container.encode(account, forKey: .account)
            case .name:
                try container.encode(name, forKey: .name)
                
            }
        }
    }
    
    selectedCodingKeys
    是结构中的新属性

     var selectedCodingKeys = [CodingKeys]()
    
    我们还可以添加一个用于编码的特定函数

    mutating func encode(for codingKeys: [CodingKeys]) throws -> Data {
        self.selectedCodingKeys = codingKeys
        return try JSONEncoder().encode(self)
    }
    
    然后解码可以像这个例子中那样进行

    var user = User(id: 1, name: "John", account: Account(name: "main", balance: 100.0))
    do {
        let data1 = try user.encode(for: [.id, .account])
        let data2 = try user.encode(for: [.id, .name])
    } catch {
        print(error)
    }
    

    在Joakim Danielson的基础上展开,您可以使用
    codingUserInfo键
    将数据传递给
    jsonecoder
    userInfo
    属性

    extension CodingUserInfoKey {
        static let keysToEncode = CodingUserInfoKey(rawValue: "keysToEncode")!
    }
    
    extension JSONEncoder {
        func withEncodeSubset<CodingKeys>(keysToEncode: [CodingKeys]) -> JSONEncoder {
            userInfo[.keysToEncode] = keysToEncode
            return self
        }
    }
    
    然后像这样使用它:

    user.name = "jon"; // assume the model was modified to make this mutable
    let jsonEncoding = JSONEncodeSubset.of(
        user, // instance to encode
        keys: [User.CodingKeys.id, User.CodingKeys.name] // only encode id & name
    );
    
    let encoder = JSONEncoder().withEncodeSubset(keysToEncode: [User.CodingKeys.id, .account])
    let encoded = try encoder.encode(user)
    
    print(String(data: encoded, encoding: .utf8)!)
    // prints: {"db_id":1,"account":{"name":"checking","balance":15000}}
    

    See和@Rob我了解如何在模型需要时提供自定义编码/解码映射。我特别关注编码部分;我需要根据应用程序不同部分中编码的数据动态编码。解码部分可以在上面的链接中实现,没有问题。