Swift 4から初めてCodable
プロトコルを使用しています。decodeIfPresent
からのDecodable
の使用を理解できません。
/// 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
に設定されるため、オプションプロパティとの違い.
これらの2行のコードには微妙ですが、重要な違いがあります。
// Exhibit 1
foo = try container.decode(Int?.self, forKey: .foo)
// Exhibit 2
foo = try container.decodeIfPresent(Int.self, forKey: .foo)
図1は解析します:
{
"foo": null,
"bar": "something"
}
しかしnot:
{
"bar": "something"
}
一方、展示2は両方を喜んで解析します。したがって、JSON
パーサーの通常の使用例では、モデルのすべてのオプションに対してdecodeIfPresent
が必要になります。
はい、@ 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
として設定します。