我最近开始学习Swift,现在我试图安全地解开来自JSON响应的多个变量,这些变量可能包含或不包含该键。
JSON响应示例:
{
"products: [{
"foo": "foo"
"bar": "bar"
}, {
"foo": "foo"
}]
}
在这里我尝试以下操作:
let dataTask = URLSession.shared.dataTask(with: myURL) { (data, response, error) in
guard let safeData = data else { return }
do {
let json = try JSONSerialization.jsonObject(with: safeData, options: .mutableLeaves)
if let jsonDict = json as? [String : Any] {
let productArray = jsonDict["products"] as? [[String : Any]]
for product in productArray! {
if let foo = product["foo"] as? String, let bar = product["bar"] as? String {
let prod = Product(foo: foo, bar: bar)
products.append(prod)
}
}
}
} catch {
print ("Error: \(error)")
}
}
我想做的是为bar
提供一个默认值(coalescing),如果该值为nil,例如"Not Available"
,以便在标签中显示它。
有可能吗?我该怎么办?
答案 0 :(得分:2)
您可以尝试
let prod = Product(foo:product["foo"] as? String ?? "Not Available" , bar: product["bar"] as? String ?? "Not Available" )
struct Root: Decodable {
let products: [Product]
}
struct Product: Decodable {
let foo: String
let bar: String?
enum CodingKeys: String, CodingKey {
case foo , bar
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
let foo = try container.decode(String.self, forKey: .foo)
let bar = try container.decodeIfPresent(String.self, forKey: .bar) ?? "Not Available"
self.init(foo:foo, bar:bar)
} catch let error {
print(error)
throw error
}
}
}
答案 1 :(得分:1)
由于json中的键是Decodable
友好的,因此您可以使用此最小代码,
struct ProductResponse: Decodable {
let products: [Product]
}
struct Product: Decodable {
let foo: String
let bar: String?
}
let dataTask = URLSession.shared.dataTask(with: myURL) { (data, response, error) in
guard let data = data else { return }
do {
let productResponse = JSONDecoder().decode(ProductResponse.self, from: data)
print(productResponse.products.forEach({ print($0.foo)}))
} catch {
print ("Error: \(error)")
}
}
在解析级别将默认值分配给bar
似乎并不自然。 Product
是仅具有两个属性的简单类型,但是对于具有数十个属性的类型,您讨厌由于一个或两个属性而实现init(from decoder: Decoder)
和CodingKeys
enumerations
需要默认值。
我建议通过向extension
引入Optional
如下所示的更好的方法,
extension Optional where Wrapped == String {
/// Unwrapped string
/// `u7d` comes from starting `u` in `Unwrapped`,
/// 7 letters in between and last letter `d`.
public func u7d(_ defaultString: String = "N/A") -> String {
guard let value = self, value.isEmpty == false else { return defaultString }
return value
}
}
因此,如果此属性为value
,现在要使用默认nil
时,只需通过如下所示传递该默认value
即可取消包装
productResponse.products.forEach({ product in
print(product.bar.u7d("Not Available"))
})
这具有以下主要优点,
if statement
进行比较时,您的nil
结果将保持预期。if statement
。UITextField
,UITextView
和UILabel
接受Optional
文本,在许多情况下,当placeholder
时,您需要显示string
属性是api响应中的nil
。因此,在这些情况下,您无需重新设计string
属性即可知道它是否具有默认值或api返回值。