Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-5981] Decodable: Decoding TimeZone within class hierarchy leads to bad container #48540

Closed
swift-ci opened this issue Sep 25, 2017 · 6 comments
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-5981
Radar rdar://problem/34634186
Original Reporter nighthawk (JIRA User)
Type Bug
Status Closed
Resolution Duplicate

Attachment: Download

Environment

Tested in Xcode Version 9.0 (9A235) on macOS 10.12.6.

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee @itaiferber
Priority Medium

md5: 3fcd4b23dd102bfc9d281ab771963642

duplicates:

  • SR-6408 JSONDecoder internal state could be corrupted

Issue Description:

I have this setup:

  • Struct S with a TimeZone field

  • Class A has a primitive field

  • Class B is a subclass of A and adds a field of type S

Decoding A succeeds, decoding S succeeds, but decoding B fails as the primitive field can't be found anymore:

keyNotFound(__lldb_expr_143.Top.(CodingKeys in _789A9D1EA1D3A02AF47559B43EA564BB).primitive, Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key primitive (\"primitive\").", underlyingError: nil))

It works as expected when changing the TimeZone field to some other type. It also works when adding that struct directly to A and not using the class hierarchy.

Playground with sample code to reproduce the issue is attached.

@belkadan
Copy link
Contributor

@itaiferber, another for you.

@swift-ci
Copy link
Collaborator Author

Comment by Adrian Schoenig (JIRA)

A little bit more on this: when I debugged this, stepping through the `decode` call in the superclass and looking at the container, it is indeed missing the coding key in question (`primitive`) and instead has the coding keys of the struct - rather than the coding keys of the class being decoded.

@itaiferber
Copy link
Contributor

This can be pared down a little bit (because it actually doesn't have much to do with the class hierarchy):

import Foundation

struct TimeZoneField : Decodable {
    let timeZone: TimeZone
    
    private enum CodingKeys : String, CodingKey {
        case timeZone
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        do {
            timeZone = try container.decode(TimeZone.self, forKey: .timeZone)
        } catch DecodingError.typeMismatch(_, _) {
            let identifier = try container.decode(String.self, forKey: .timeZone)
            timeZone = TimeZone(identifier: identifier)!
        }
    }
}

struct X : Decodable {
    private enum CodingKeys : String, CodingKey {
        case a, b
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let a = try container.decode(TimeZoneField.self, forKey: .a)

        // Works
        let b = try container.decode(Double.self, forKey: .b)
        
        // Does not work:
//        let container2 = try decoder.container(keyedBy: CodingKeys.self)
//        let b = try container2.decode(Double.self, forKey: .b)
        
        print("a: \(a)")
        print("b: \(b)")
    }
}

let json = "{ \"a\": { \"timeZone\": \"Australia/Sydney\" }, \"b\": 34.2 }".data(using: .utf8)!
let x = try JSONDecoder().decode(X.self, from: json)

This doesn't appear to happen if a is a primitive type, e.g., a Double. Looking into this a bit further now.

@itaiferber
Copy link
Contributor

@swift-ci Create

@swift-ci
Copy link
Collaborator Author

swift-ci commented Jan 2, 2018

Comment by Thorsten Karrer (JIRA)

Probably the same cause as SR-6563. If you do not let the first call to container.decode() in TimeZoneField.init(from: ) fail (e.g. by trying decoding by identifier first, then try decoding the timezone directly in the catch block), it works. Smells like the broken container stack after exiting the scope via try before popping the top element (see comments in SR-6563).

@itaiferber
Copy link
Contributor

Confirmed that this is indeed a dupe of SR-6563 and thus a dupe of SR-6408. This should be fixed with the fixes there too.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself
Projects
None yet
Development

No branches or pull requests

3 participants