说我有这样的功能:
private func setObject<T:Encodable>(object:T, updater:T)
{
let encoder = JSONEncoder()
do
{
let objectData = try encoder.encode(object)
let updateData = try encoder.encode(updater)
样本对象的位置是:
public struct Something : Codable
{
public var property1:String?
public var property2:String?
}
和对象的示例实例将是
object.property1 == "A"
object.property2 == "B"
和更新者:
updater.property1 == "C"
updater.property2 == nil
是否有快速创建对象的方法:
newObject.property1 == "C"
newObject.property2 == "B"
答案 0 :(得分:1)
请记住,生成的Something.encode(to:)
方法将调用encoder.container(keyedBy: CodingKeys.self)
来获取容器,并将其属性编码到该容器中。因此,您需要使object
和update
使用相同的容器进行自我编码。然后,update
的属性可能会覆盖object
的属性。另一方面,编码器可能会抛出错误。让我们试一试,看看。
为了使它们使用相同的容器,我们将创建一个包含对象和更新的包装类型。
import Foundation
public struct Something : Codable
{
public var property1:String?
public var property2:String?
}
struct UpdateWrapper {
var something: Something
var update: Something
}
现在我们将UpdateWrapper
符合Encodable
。如果我们让编译器生成一致性,它将如下所示:
// What the compiler would generate, but NOT what we want.
extension UpdateWrapper: Encodable {
enum CodingKeys: String, CodingKey {
case something
case update
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(something, forKey: .something)
try container.encode(update, forKey: .update)
}
}
我们之所以不希望这样,是因为这会导致something
和update
从编码器中获取单独的容器,我们希望它们进入同一个容器。因此,我们将遵循这样的Encodable
:
extension UpdateWrapper: Encodable {
func encode(to encoder: Encoder) throws {
try something.encode(to: encoder)
try update.encode(to: encoder)
}
}
通过这种方式,something
和encoder
每个都会收到相同的Encoder
codingPath
,因此他们从编码器获得相同的容器。我们可以这样测试:
let original = Something(property1: "A", property2: "B")
let update = Something(property1: "C", property2: nil)
let encoder = JSONEncoder()
let jsonData = try! encoder.encode(UpdateWrapper(something: original, update: update))
let jsonString = String(data: jsonData, encoding: .utf8)!
print("jsonString=\(jsonString)")
let decoder = JSONDecoder()
let reconstructed = try! decoder.decode(Something.self, from: jsonData)
print("reconstructed=\(reconstructed)")
这是输出:
jsonString={"property2":"B","property1":"C"}
reconstructed=Something(property1: Optional("C"), property2: Optional("B"))
所以它确实有效。这是个好主意吗?我不知道。我不确定是否在任何地方指定您实际上允许使用相同的密钥对同一个容器进行两次编码。
答案 1 :(得分:0)
约束Encodable
并没有提供足够的信息。您可以创建单独的协议来更新对象。
protocol Updatable {
func update(_ updatedObject: Self) -> Self
}
public struct Something: Updatable {
var property1: String?
var property2: String?
func update(_ updatedObject: Something) -> Something {
return Something(
property1: updatedObject.property1 ?? property1,
property2: updatedObject.property2 ?? property2)
}
}
func setObject<T: Updatable>(object: T, updater: T) -> T {
return object.update(updater)
}