我刚才在查看编码器协议时有了顿悟。
KeyedEncodingContainerProtocol.superEncoder(forKey:)
方法正好适用于这种情况。
此方法返回一个单独的
Encoder
它可以收集多个项目和/或嵌套容器,然后将它们编码为一个键。
对于这个特定的案例,最高层
user
数据可以通过简单地调用自己的
encode(to:)
方法,使用新的
superEncoder
. 然后,也可以使用编码器创建嵌套容器,以便正常使用。
问题的解决方案
// MARK: - Encodable
func encode(to encoder: Encoder) throws {
var baseContainer = encoder.container(keyedBy: CodingKeys.self)
try baseContainer.encode(self.result, forKey: .result)
// MARK: - PROBLEM!!
// // This is overwritten
// try baseContainer.encodeIfPresent(self.data, forKey: .data)
//
// // This overwrites the previous statement
// var dataContainer = baseContainer.nestedContainer(keyedBy: DataDictKeys.self, forKey: .data)
// try dataContainer.encodeIfPresent(self.user, forKey: .user)
// MARK: - Solution
// Create a new Encoder instance to combine data from separate sources.
let dataEncoder = baseContainer.superEncoder(forKey: .data)
// Use the Encoder directly:
try self.data?.encode(to: dataEncoder)
// Create containers for manually encoding, as usual:
var userContainer = dataEncoder.container(keyedBy: DataDictKeys.self)
try userContainer.encodeIfPresent(self.user, forKey: .user)
}
输出:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>data</key>
<dict>
<key>order_count</key>
<integer>10</integer>
<key>user</key>
<dict>
<key>name</key>
<string>John</string>
</dict>
</dict>
<key>result</key>
<string>OK</string>
</dict>
</plist>