Skip to content

[SR-7109] JSONDecoder fails to pop codingPath entry after exception #4252

Open
@swift-ci

Description

@swift-ci
Previous ID SR-7109
Radar None
Original Reporter pdwilson12 (JIRA User)
Type Bug
Additional Detail from JIRA
Votes 0
Component/s Foundation
Labels Bug
Assignee @itaiferber
Priority Medium

md5: cbee189340585f8ddc888fbf34a8951f

Issue Description:

When decoding a structured JSON file in which a particular field can be either an array or a dictionary, I detect which is the case by asking the decoder for the unkeyed container and then if it generates an exception, try the keyed container:

{{ for key in keyedContainer.allKeys {}}
{{ switch key {}}
{{ case .items:}}
{{ var nested = try? keyedContainer.nestedUnkeyedContainer(forKey: key)}}
{{ if nested == nil {}}
{{ let singleItem = try? keyedContainer.decode(JSRefWrapper.self, forKey: key)}}

After this code runs and hits an exception in the first "try?", the data returned from the decoder's codingPath incorrectly lists the "items" element twice.

Looking at the implementation of JSONDecoder, I found this code multiple times in JSONEncoder.swift:

{{ fileprivate func with<T>(pushedKey key: CodingKey, _ work: () throws -> T) rethrows -> T {}}
{{ self.codingPath.append(key)}}
{{ let ret: T = try work()}}
{{ self.codingPath.removeLast()}}
{{ return ret}}
{{ }}}

If the work() block throws an exception (as is done by nestedUnkeyedContainer()), the removeLast() call will not be executed.

I think removeLast should be put into a 'defer' block, which will cause it to be executed even on an exception...

{{ fileprivate func with<T>(pushedKey key: CodingKey, _ work: () throws -> T) rethrows -> T {}}
{{ self.codingPath.append(key)}}
{{ defer { self.codingPath.removeLast()}}
{{ let ret: T = try work()}}
{{ return ret}}
{{ }}}

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions