Decodable: filter items in array that fail to decode

283 Views Asked by At

How can I ensure that I only filter out Person B below since Person B is the only malformed object?

The documentation for currentIndex on UnkeyedDecodingContainer states:

The current decoding index of the container (i.e. the index of the next element to be decoded.) Incremented after every successful decode call.

It seems that if you try and filter a decoding failure using try?, it never progresses to the next element in the array.

But how would one achieve this? Note that the structure of the JSON cannot be changed.

let json = """
{
    "collection" : [
        {
            "name" : "A",
            "surname" : "ob"
        },
        {
            "name" : "B"
        },
        {
            "name" : "C",
            "surname" : "ob"
        },
        {
            "name" : "D",
            "surname" : "ob"
        }
    ]
}
""".data(using: .utf8)!

struct Person: Decodable {
    let name: String
    let surname: String
}

struct Collection: Decodable {
    let items: [Person]

    private enum CodingKeys: String, CodingKey {
        case items = "collection"
    }

    public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)

        var itemsArr: [Person] = []

        var itemValues = try values.nestedUnkeyedContainer(forKey: .items)
        if let count = itemValues.count {
            for val in (0..<count) {
                print("trying \(val) at index: \(itemValues.currentIndex)")
                if let element = try? itemValues.decode(Person.self) {
                    print("adding \(val)")
                    itemsArr.append(element)
                }
            }
        }

        items = itemsArr
    }
}

let decoder = JSONDecoder()
let collection = try decoder.decode(Collection.self, from: json)
print("collection: \(collection)")

Produces the output:

trying 0 at index: 0
adding 0
trying 1 at index: 1
trying 2 at index: 1
trying 3 at index: 1
collection: Collection(items: [__lldb_expr_101.Person(name: "A", surname: "ob")])
0

There are 0 best solutions below