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-9023] Decoding a JSON Dictionary with a UUID key either doesn't work, or shows the wrong error message #3614

Open
swift-ci opened this issue Oct 17, 2018 · 4 comments

Comments

@swift-ci
Copy link
Contributor

@swift-ci swift-ci commented Oct 17, 2018

Previous ID SR-9023
Radar None
Original Reporter mayurdhaka.swift (JIRA User)
Type Bug

Attachment: Download

Environment

I ran the code in a Playground file and in an iOS app as well.
Xcode Version: 10.0 (10A255)
macOS Version: 10.14 (18A391)
Swift Version: 4.2

Additional Detail from JIRA
Votes 1
Component/s Foundation
Labels Bug, Codable
Assignee @itaiferber
Priority Medium

md5: 6739ca72461dc717465a4d9b646ad032

is duplicated by:

  • SR-9965 Decodable: type mismatch error when decoding string to date as dictionary key

Issue Description:

Consider the following attempt to deserialise JSON:

 import UIKit

let json = """
 {
     "localisedTexts": {
         "454198ea-bee1-49ac-a8b9-1ceaf8220a85": {
             "EN-US": "What is your name?", "EN-UK": "What is your name?"
         }
      }
 }
 """.data(using: .utf8)!

struct MyStruct: Codable {
   let localisedTexts: Dictionary<UUID, Dictionary<String, String>> 
}

let jsonDecoder = JSONDecoder()

do {
    let x = try jsonDecoder.decode(MyStruct.self, from: json)
    print(x)
} catch let err {
   print(err)
}

I would expect my object to get deserialised from the json. Instead, I get this error message:
typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "localisedTexts", intValue: nil)], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))


Here's some other things I've tried:

  • Changing `let localisedTexts: Dictionary<UUID, Dictionary<String, String>>` to `let localisedTexts: Dictionary<String, Dictionary<String, String>>` deserialises my object just fine.

  • Changing `UUID` to `Int` for the key (and changing the JSON correspondingly) works too.

I've attached a playground file for illustrative purpose.

@belkadan
Copy link

@belkadan belkadan commented Oct 26, 2018

A UUID isn't actually a String, even though it can be encoded with one. @itaiferber can explain more why this isn't supported.

@swift-ci
Copy link
Contributor Author

@swift-ci swift-ci commented Oct 26, 2018

Comment by Mayur Dhaka (JIRA)

@belkadan Thanks for responding.

I see your point but I tried decoding a UUID when it lies in the 'value' part of a key-value pair and that works fine. This seems like inconsistent behaviour to me?

Either way, the error message displayed by the decoder isn't indicative of the problem.

@itaiferber
Copy link
Contributor

@itaiferber itaiferber commented Oct 30, 2018

To give a bit more detail — the implementation for Dictionary's Codable conformance has a choice to make: it can either encode its keys and values into a KeyedEncodingContainer or an UnkeyedEncodingContainer. If it uses a KeyedEncodingContainer, the only way it can do so is if it is able to convert its keys into {{CodingKey}}s, and that's only really possible if the key type is already either String or Int. Dictionary can't know ahead of time how UUID would choose to encode itself (or be encoded), nor can it easily ask to "just encode" UUID and check for its representation after the fact.

This means that even for types like UUID, which would encode as a String, the only thing Dictionary can do is fall back to UnkeyedEncodingContainer. Decoding matches this structure — since Dictionary can't get a KeyedDecodingContainer keyed by {{UUID}}s, there's no way it could convert those strings to {{UUID}}s.

I agree that the error message is not terribly helpful, and we can likely do a better job there.

@itaiferber
Copy link
Contributor

@itaiferber itaiferber commented Oct 30, 2018

In any case, your best bet is to override MyStruct.init(from🙂/MyStruct.encode(to🙂 to map those {{UUID}}s to {{String}}s and vice versa.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@shahmishal shahmishal transferred this issue from apple/swift May 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants