Skip to content

[SR-5953] Decodable: Allow "Lossy" Array Decodes #4414

Description

@swift-ci
Previous ID SR-5953
Radar rdar://problem/37316307
Original Reporter cocoahero (JIRA User)
Type Improvement

Attachment: Download

Additional Detail from JIRA
Votes 26
Component/s Foundation
Labels Improvement
Assignee None
Priority Medium

md5: 8e5158944f566da394566f3054ff764a

Issue Description:

In the current implementation of Decodable, it is impossible to attempt to decode an array of Decodable's and allow individual items to fail.

For example:

enum Season: String, Decodable {
    case winter
    case spring
    case summer
    case autumn
}

let json = """
["winter", "spring", "summer", "fall"]
""".data(using: .utf8)!

let seasons = try! JSONDecoder().decode([Season].self, from: json)

This will raise an error of the following:

Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 3", intValue: Optional(3))], debugDescription: "Cannot initialize Season from invalid String value fall", underlyingError: nil))

It may be considered desirable in certain use cases that this decode succeeds, but omits individual items that fail. As such, the desired output value would be:

[.winter, .spring. summer]

One may attempt to circumvent this by using an outer, wrapper type combined with a customized initializer that ignores inner decode errors, such as the following:

struct Climate: Decodable {
    var seasons: [Season]

    enum CodingKeys: String, CodingKey {
        case seasons
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        var seasonsContainer = try container.nestedUnkeyedContainer(forKey: .seasons)

        var seasons = [Season]()
        while !seasonsContainer.isAtEnd {
            if let season = try? seasonsContainer.decode(Season.self) {
                seasons.append(season)
            }
        }
        self.seasons = seasons
    }
}

let json = """
{ "seasons": ["winter", "spring", "summer", "fall"] }
""".data(using: .utf8)!

let climate = try! JSONDecoder().decode(Climate.self, from: json)

However, the above code will result in an infinite loop due to the implementation of UnkeyedDecodingContainer.currentIndex. This is because the currentIndex is not incremented unless a decode succeeds.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions