В чем разница между опцией и 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.