Swift 在JSON解析中使用Decodable时,optional和decodeIfPresent之间有什么区别?
我第一次使用Swift 4中的Swift 在JSON解析中使用Decodable时,optional和decodeIfPresent之间有什么区别?,swift,swift4,codable,decodable,Swift,Swift4,Codable,Decodable,我第一次使用Swift 4中的Codable协议,我无法理解decodeIfPresent中的decodeIfPresent的用法 /// Decodes a value of the given type for the given key, if present. /// /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is
Codable
协议,我无法理解decodeIfPresent
中的decodeIfPresent
的用法
/// Decodes a value of the given type for the given key, if present.
///
/// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call.
///
/// - parameter type: The type of value to decode.
/// - parameter key: The key that the decoded value is associated with.
/// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value.
/// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type.
public func decodeIfPresent(_ type: String.Type, forKey key: KeyedDecodingContainer.Key) throws -> String?
在这里,它建议如果相关键不存在值,则返回
nil
。如果这是唯一的原因,那么它与可选属性有何不同,因为如果响应中不存在值,可选变量也会设置为nil
是的,@Sweeper的评论很有道理
我会根据我的理解来解释
public class User : Decodable{
public var firstName:String
public var lastName:String
public var middleName:String?
public var address:String
public var contactNumber:String
public enum UserResponseKeys: String, CodingKey{
case firstName = "first_name"
case lastName = "last_name"
case middleName = "middle_name"
case address = "address"
case contactNumber = "contact_number"
}
public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: UserResponseKeys.self)
self.firstName = try container.decode(String.self, forKey: .firstName)
self.lastName = try container.decode(String.self, forKey: .lastName)
self.middleName = try container.decodeIfPresent(String.self, forKey: .middleName)
self.address = try container.decode(String.self, forKey: .address)
self.contactNumber = try container.decode(String.self, forKey: .contactNumber)
}
}
上面是我的User
类,其中我将middleName
标记为可选参数,因为JSON响应可能不会在响应中提供middleName
键值对,所以我们可以使用decodeIfPresent
self.middleName = try container.decodeIfPresent(String.self, forKey: .middleName)
而对于其他属于必填字段的变量,我们确信不需要使用可选字段。我们只使用了decode
,因为该方法不返回可选值
public func decode(_ type: String.Type, forKey key: KeyedDecodingContainer.Key) throws -> String
上面的decode
函数返回String
,而decodeIfPresent
返回String?
,因此我们可以使用可选变量来存储它
因此,最后的结论是,如果您不确定服务响应契约,或者您可能处理JSON响应和参数可能会在您不知情的情况下更改的任何第三方服务,那么您可以使用
decodeIfPresent
,以便它可以处理响应中缺少特定参数的情况,并将值设置为nil
这两行代码之间有一个微妙但重要的区别:
//附件1
foo=try container.decode(Int?.self,forKey:.foo)
//附件2
foo=try container.decodeIfPresent(Int.self,forKey:.foo)
附件1将分析:
{
“foo”:空,
“酒吧”:“某物”
}
但是不是:
{
“酒吧”:“某物”
}
而图表2将很高兴地解析这两个。因此,在
JSON
解析器的正常使用情况下,对于模型中的每个可选属性,您都需要decodeIfPresent
。我认为,如果您希望对JSON中可能缺少的属性使用默认值,那么使用decodeIfPresent
而不是可选属性是有意义的
例如,让我们检查3种情况:
1。所有密钥都存在于JSON中:
{
"project_names": ["project1", "project2", "project3"]
}
假设您必须解码此JSON:
{
"project_names": ["project1", "project2", "project3"],
"is_pro": true
}
您可以使用此结构:
struct Program: Codable {
let projectNames: [String]
let isPro: Bool
}
struct Program: Codable {
let projectNames: [String]
var isPro: Bool?
}
您将得到一个Program
对象,其isPro
值等于true
。
(我假设您的解码器keyDecodingStrategy
在本例的其余部分是.convertFromSnakeCase
)
2。JSON中缺少一些键,您可以在Swift中使用可选键:
{
"project_names": ["project1", "project2", "project3"]
}
现在可以使用此结构:
struct Program: Codable {
let projectNames: [String]
let isPro: Bool
}
struct Program: Codable {
let projectNames: [String]
var isPro: Bool?
}
您将得到一个Program
对象,其isPro
值等于nil
如果JSON看起来像这样:
{
"project_names": ["project1", "project2", "project3"],
"is_pro": true
}
struct Program: Codable {
let projectNames: [String]
var isPro: Bool = false
}
然后,isPro
将是一个值为true的Bool?
。
也许这就是您想要的,但您可能希望使用默认值为false
的Bool
。这就是decodeIfPresent
可能有用的地方
3。JSON中缺少一些键,您需要一个在Swift中具有默认值的非可选属性:
{
"project_names": ["project1", "project2", "project3"]
}
如果您的结构如下所示:
{
"project_names": ["project1", "project2", "project3"],
"is_pro": true
}
struct Program: Codable {
let projectNames: [String]
var isPro: Bool = false
}
如果JSON中不存在“is_pro”属性,则会出现解析错误因为Codable希望找到一个值来解析Bool属性。
在这种情况下,一个好主意是使用带有decodeIfPresent
的初始值设定项,如下所示:
struct Program: Codable {
let projectNames: [String]
let isPro: Bool
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.projectNames = try container.decode([String].self, forKey: .projectNames)
self.isPro = try container.decodeIfPresent(Bool.self, forKey: .isPro) ?? false
}
}
这使您能够在两个方面都做到最好:
- 您的结构具有
Bool
,而不是Bool?
属性
- 您仍然能够解析不包含“is_pro”字段的JSON
- 如果JSON中不存在该字段,则可以获得默认值
false
您是否知道该方法返回一个可选的字符串?
?所以基本上你是对的。有一个选项的使用。找到答案:虽然在文档中没有提到。在问题D中也可以找到它本身。如果遇到的编码值不能转换为请求的类型,它将抛出DecodingError.typeMismatch
。解码
也会抛出三种不同的错误。请参见第一个响应能否由foo=try container.decode(Int.self,forKey.foo)
解析?否,它不能用foo=try container.decode(Int.self,forKey.foo)
解析,因为Int
不能保存null
值(即nil
)。在foo=try container.decodeIfPresent(Int.self,forKey.foo)
和foo=try之间是否存在差异?container.decode(Int.self,forKey.foo)
?是的,因为foo=try?container.decode(Int.self,forKey.foo)
将对中“foo”
包含值但无法转换为Int
的所有事件进行解码。例如:“foo”:“某物”
将导致foo
包含nil
。您肯定应该捕获错误,而不是通过try?
忽略它。