एक विकल्प एक रैपर प्रकार का उपयोग करना है जो किसी दिए गए मूल्य को डिकोड करने का प्रयास करता है; nil
असफल होने पर भंडारण करना :
struct FailableDecodable<Base : Decodable> : Decodable {
let base: Base?
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.base = try? container.decode(Base.self)
}
}
हम तब प्लेसहोल्डर GroceryProduct
में आपके भरने के साथ इनमें से एक सरणी को डीकोड कर सकते हैं Base
:
import Foundation
let json = """
[
{
"name": "Banana",
"points": 200,
"description": "A banana grown in Ecuador."
},
{
"name": "Orange"
}
]
""".data(using: .utf8)!
struct GroceryProduct : Codable {
var name: String
var points: Int
var description: String?
}
let products = try JSONDecoder()
.decode([FailableDecodable<GroceryProduct>].self, from: json)
.compactMap { $0.base } // .flatMap in Swift 4.0
print(products)
// [
// GroceryProduct(
// name: "Banana", points: 200,
// description: Optional("A banana grown in Ecuador.")
// )
// ]
हम तब .compactMap { $0.base }
फ़िल्टर करने के लिए उपयोग कर रहे हैंnil
तत्वों हैं (जो डिकोडिंग पर एक त्रुटि फेंकते हैं)।
यह एक मध्यवर्ती सरणी बनाएगा [FailableDecodable<GroceryProduct>]
, जो एक मुद्दा नहीं होना चाहिए; हालाँकि यदि आप इससे बचना चाहते हैं, तो आप हमेशा एक और रैपर प्रकार बना सकते हैं जो एक तत्व को हटा देता है और प्रत्येक तत्व को अनवाक्ड सेल से हटा देता है:
struct FailableCodableArray<Element : Codable> : Codable {
var elements: [Element]
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var elements = [Element]()
if let count = container.count {
elements.reserveCapacity(count)
}
while !container.isAtEnd {
if let element = try container
.decode(FailableDecodable<Element>.self).base {
elements.append(element)
}
}
self.elements = elements
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(elements)
}
}
फिर आप निम्नानुसार डीकोड करेंगे:
let products = try JSONDecoder()
.decode(FailableCodableArray<GroceryProduct>.self, from: json)
.elements
print(products)
// [
// GroceryProduct(
// name: "Banana", points: 200,
// description: Optional("A banana grown in Ecuador.")
// )
// ]