Swift 可解码可选密钥
Swift Decodable Optional Key
(这是这个问题的后续问题:Using Decodable protocol with multiples keys。)
我有以下 Swift 代码:
let additionalInfo = try values.nestedContainer(keyedBy: UserInfoKeys.self, forKey: .age)
age = try additionalInfo.decodeIfPresent(Int.self, forKey: .realage)
我知道如果我使用 decodeIfPresent
而没有 属性 如果它是一个可选变量,它仍然会正确处理它。
例如下面的 JSON 使用上面的代码解析它。
{
"firstname": "Test",
"lastname": "User",
"age": {"realage": 29}
}
下面的 JSON 也适用。
{
"firstname": "Test",
"lastname": "User",
"age": {"notrealage": 30}
}
但是下面的不行。
{
"firstname": "Test",
"lastname": "User"
}
我怎样才能使所有 3 个示例都起作用? nestedContainer
是否有类似于 decodeIfPresent
的内容?
您能否尝试将示例 JSON 粘贴到 quicktype 中以查看它推断出的类型?根据你的问题,我粘贴了你的示例并得到:
struct UserInfo: Codable {
let firstname: String
let age: Age?
let lastname: String
}
struct Age: Codable {
let realage: Int?
}
使 UserInfo.age
和 Age.realage
选项有效,如果这是您想要完成的。
您可以使用以下KeyedDecodingContainer
函数:
func contains(_ key: KeyedDecodingContainer.Key) -> Bool
Returns a Bool
value indicating whether the decoder contains a value associated with the given key. The value associated with the given key may be a null value as appropriate for the data format.
例如,在请求相应的嵌套容器之前检查"age"
键是否存在:
struct Person: Decodable {
let firstName, lastName: String
let age: Int?
enum CodingKeys: String, CodingKey {
case firstName = "firstname"
case lastName = "lastname"
case age
}
enum AgeKeys: String, CodingKey {
case realAge = "realage"
case fakeAge = "fakeage"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.firstName = try values.decode(String.self, forKey: .firstName)
self.lastName = try values.decode(String.self, forKey: .lastName)
if values.contains(.age) {
let age = try values.nestedContainer(keyedBy: AgeKeys.self, forKey: .age)
self.age = try age.decodeIfPresent(Int.self, forKey: .realAge)
} else {
self.age = nil
}
}
}
我遇到了这个问题,我找到了这个解决方案,以防对其他人有帮助:
let ageContainer = try? values.nestedContainer(keyedBy: AgeKeys.self, forKey: .age)
self.age = try ageContainer?.decodeIfPresent(Int.self, forKey: .realAge)
如果你有一个可选的容器,使用 try? values.nestedContainer(keyedBy:forKey)
你不需要使用 contains(
检查容器是否存在。
(这是这个问题的后续问题:Using Decodable protocol with multiples keys。)
我有以下 Swift 代码:
let additionalInfo = try values.nestedContainer(keyedBy: UserInfoKeys.self, forKey: .age)
age = try additionalInfo.decodeIfPresent(Int.self, forKey: .realage)
我知道如果我使用 decodeIfPresent
而没有 属性 如果它是一个可选变量,它仍然会正确处理它。
例如下面的 JSON 使用上面的代码解析它。
{
"firstname": "Test",
"lastname": "User",
"age": {"realage": 29}
}
下面的 JSON 也适用。
{
"firstname": "Test",
"lastname": "User",
"age": {"notrealage": 30}
}
但是下面的不行。
{
"firstname": "Test",
"lastname": "User"
}
我怎样才能使所有 3 个示例都起作用? nestedContainer
是否有类似于 decodeIfPresent
的内容?
您能否尝试将示例 JSON 粘贴到 quicktype 中以查看它推断出的类型?根据你的问题,我粘贴了你的示例并得到:
struct UserInfo: Codable {
let firstname: String
let age: Age?
let lastname: String
}
struct Age: Codable {
let realage: Int?
}
使 UserInfo.age
和 Age.realage
选项有效,如果这是您想要完成的。
您可以使用以下KeyedDecodingContainer
函数:
func contains(_ key: KeyedDecodingContainer.Key) -> Bool
Returns a
Bool
value indicating whether the decoder contains a value associated with the given key. The value associated with the given key may be a null value as appropriate for the data format.
例如,在请求相应的嵌套容器之前检查"age"
键是否存在:
struct Person: Decodable {
let firstName, lastName: String
let age: Int?
enum CodingKeys: String, CodingKey {
case firstName = "firstname"
case lastName = "lastname"
case age
}
enum AgeKeys: String, CodingKey {
case realAge = "realage"
case fakeAge = "fakeage"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.firstName = try values.decode(String.self, forKey: .firstName)
self.lastName = try values.decode(String.self, forKey: .lastName)
if values.contains(.age) {
let age = try values.nestedContainer(keyedBy: AgeKeys.self, forKey: .age)
self.age = try age.decodeIfPresent(Int.self, forKey: .realAge)
} else {
self.age = nil
}
}
}
我遇到了这个问题,我找到了这个解决方案,以防对其他人有帮助:
let ageContainer = try? values.nestedContainer(keyedBy: AgeKeys.self, forKey: .age)
self.age = try ageContainer?.decodeIfPresent(Int.self, forKey: .realAge)
如果你有一个可选的容器,使用 try? values.nestedContainer(keyedBy:forKey)
你不需要使用 contains(
检查容器是否存在。