В чем разница между опцией и decodeIfPresent при использовании Decodable for JSON Parsing?
Я использую протокол Codable
от Swift 4 в первый раз, я не могу понять использование 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
если в ответе нет значения.
Ответы
Ответ 1
Там тонкое, но важное различие между этими двумя строками кода:
// 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"
}
но не:
{
"bar": "something"
}
в то время как выставка 2 будет радостно разбирать и то, и другое. Поэтому в обычных случаях использования для парсеров JSON
вам понадобится decodeIfPresent
для каждой опции в вашей модели.
Ответ 2
Да, комментарий @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
ключ-значение 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
.