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 }
}
我有以下型号:
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 }
}