Une option consiste à utiliser un type wrapper qui tente de décoder une valeur donnée; stockant nil
en cas d'échec :
struct FailableDecodable : Decodable {
let base: Base?
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.base = try? container.decode(Base.self)
}
}
Nous pouvons ensuite décoder un tableau de ces éléments, avec votre GroceryProduct
remplissant l'espace réservé Base
:
import Foundation
let json = """
[
{
"name": "Banana",
"points": 200,
"description": "Une banane cultivée en Équateur."
},
{
"name": "Orange"
}
]
""".data(using: .utf8)!
struct GroceryProduct : Codable {
var name: String
var points: Int
var description: String?
}
let products = try JSONDecoder()
.decode([FailableDecodable].self, from: json)
.compactMap { $0.base } // .flatMap in Swift 4.0
print(products)
// [
// GroceryProduct(
// name: "Banana", points: 200,
// description: Optional("Une banane cultivée en Équateur.")
// )
// ]
Nous utilisons ensuite .compactMap { $0.base }
pour filtrer les éléments nil
(ceux qui ont généré une erreur lors du décodage).
Cela créera un tableau intermédiaire de [FailableDecodable]
, ce qui ne devrait pas poser de problème; cependant, si vous souhaitez l'éviter, vous pourriez toujours créer un autre type wrapper qui décode et déballe chaque élément à partir d'un conteneur sans clé :
struct FailableCodableArray : 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.self).base {
elements.append(element)
}
}
self.elements = elements
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(elements)
}
}
Vous pouvez alors décoder comme suit :
let products = try JSONDecoder()
.decode(FailableCodableArray.self, from: json)
.elements
print(products)
// [
// GroceryProduct(
// name: "Banana", points: 200,
// description: Optional("Une banane cultivée en Équateur.")
// )
// ]
0 votes
Nous ne pouvons pas ignorer les objets invalides, mais vous pouvez leur attribuer des valeurs par défaut s'ils sont nuls.
3 votes
Pourquoi ne peut pas
points
juste être déclaré optionnel?0 votes
Parce que parfois un champ manquant n'a pas de sens, et le rendre optionnel ruinerait votre modèle.