Swift可解码预置字段(来自JSON)

Swift Decodable pre-set field (from JSON)

我有以下型号:

struct User: Decodable {
  var user: String,
  var email: String,
  var selected: Bool = false
}

我的JSON:

{
   "user":"foo",
   "email":"foo@bar.com"
}

将 JSON 作为数据加载并将其解码为用户对象时,我遇到了失败。因为它缺少所选的键。但是,我在 User 对象本身中提供了一个默认值。有没有一种简单的方法可以在用户 json 的解析中不包含 'selected'?

例如,这失败了,因为其中一个键丢失了

let obj: User = try! JSONDecoder().decode(User.type, from: data)

(当密钥出现在 JSON 时,它可以工作,但我想向用户添加一些 'local' 数据)(例如,哪个用户是由使用该软件的人选择的) .

您可以使用自定义解码器:

struct User: Codable {
    let user: String
    let email: String
    let selected: Bool

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        user = try container.decode(String.self, forKey: .user)
        email = try container.decode(String.self, forKey: .email)

        let decodedSelected = try container.decodeIfPresent(Bool.self, forKey: .selected)
        selected = decodedSelected ?? false //  Here is the default value
    }
}

此外,您可以使用 属性 包装器,但它需要更多的解码代码。有兴趣可以看看this article

基于文章 (@propertyWrapper) 的回答:https://www.programmersought.com/article/94997602083/

protocol DefaultValue {
    associatedtype Value: Decodable
    static var defaultValue: Value { get }
}

@propertyWrapper
struct Default<T: DefaultValue> {
    var wrappedValue: T.Value
}

extension Default: Decodable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        wrappedValue = (try? container.decode(T.Value.self)) ?? T.defaultValue
    }
}

extension KeyedDecodingContainer {
    func decode<T>(
        _ type: Default<T>.Type,
        forKey key: Key
    ) throws -> Default<T> where T: DefaultValue {
        try decodeIfPresent(type, forKey: key) ?? Default(wrappedValue: T.defaultValue)
    }
}

extension Bool {
    enum False: DefaultValue {
        static let defaultValue = false
    }
    enum True: DefaultValue {
        static let defaultValue = true
    }
}

struct User: Decodable {
    let user: String
    let email: String
    @Default<Bool.True> var selected: Bool // Default Value: true
}

/// Make Network Request
typealias RequestCompletionHandler<T: Decodable> = (_ value: T?, _ error: Error?) -> Void


func callAPI<T: Decodable>(completionHandler: RequestCompletionHandler<T>) {
    let data = """
{
   "user":"foo",
   "email":"foo@bar.com"
}
""".data(using: .utf8)!
    do {
        let value = try JSONDecoder().decode(T.self, from: data)
        completionHandler(value, nil)
    } catch {
        completionHandler(nil, error)
    }
}

callAPI { (model: User?, error) in
    if let finalModel = model {
        print("\(finalModel)")
    } else if let error = error {
        print("Error: \(error)")
    }
}

只需指定 CodingKeys。所有不在枚举中的键都将被忽略。

struct User: Decodable {
  var user: String
  var email: String
  var selected: Bool = false

  private enum CodingKeys: String, CodingKey { case user, email }
}