问题描述
我有以下模型:
struct User: Decodable {
var user: String,var email: String,var selected: Bool = false
}
我的 JSON:
{
"user":"foo","email":"foo@bar.com"
}
将 JSON 作为数据加载并将其解码为 User 对象时,出现故障。因为它缺少所选的键。但是,我在 User 对象本身中提供了一个默认值。有没有一种简单的方法可以在解析用户 json 时不包含“selected”?
例如,由于缺少其中一个键而失败
let obj: User = try! JSONDecoder().decode(User.type,from: data)
(当密钥存在于 JSON 中时,它可以工作,但我想向用户添加一些“本地”数据)(例如,使用该软件的人选择了哪个用户)。
解决方法
只需指定 CodingKeys
。不在枚举中的所有键都将被忽略。
struct User: Decodable {
var user: String
var email: String
var selected: Bool = false
private enum CodingKeys: String,CodingKey { case user,email }
}
,
您可以使用自定义解码器:
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)")
}
}