-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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-7788] Enum with String or Int RawRepresentable not encoded / decoded properly #3690
Comments
Comment by Clément Nonn (JIRA) The code leading to this behavior is located here : apple/swift/blob/d2085d8b0ed69e40a10e555669bb6cc9b450d0b3/stdlib/public/core/Codable.swift.gyb#L1967 |
@swift-ci create |
Comment by Mattias Persson (JIRA) This issue still exists in Swift 5. For me it's a pretty big problem. |
Comment by Mattias Persson (JIRA) @itaiferber Are there any workarounds for this, except from the obvious "stop using enums as dictionary keys"? |
Comment by Clément Nonn (JIRA) you can write you own implementation of conformance to Decodable / Encodable using a new type like `struct EnumCodableDictionary<Key, Value>`. struct DecodingEnumArray<T: Decodable>: Decodable, ArrayConvertible, ExpressibleByArrayLiteral {
typealias ArrayElementType = T
typealias ArrayLiteralElement = T
var values: [T]
init(arrayLiteral elements: T...) {
self.values = elements
}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
guard let count = container.count else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "count not known for this array")
}
var values = [T]()
for _ in 0..<count {
do {
values.append(try container.decode(T.self))
} catch {
let error = DecodingError.dataCorruptedError(in: container, debugDescription: "entity not conformed. Aborted")
throw error
}
}
self.values = values
}
}
extension DecodingContinuableArray: Encodable where T: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(contentsOf: self.values)
}
} This sample shows an array, but you can do the same for a dictionary. |
Comment by Mattias Persson (JIRA) Thanks Zarghol (JIRA User)! Your code inspired me so I wrote this which suits my needs perfectly. |
Comment by Clément Nonn (JIRA) No problem 😉 just a little problem : in you init, when the enum value is not decoded, I think you should throw an error, not just continue silently. Else the result is not really conformed to the input.. |
Comment by Mattias Persson (JIRA) Good point! The code is written in the "app layer" and I'm thinking that the network client (which is running this code) shall survive if the backend suddenly sends a JSON containing a newly defined key/case. It's a bit of a project implementation requirement but you do have a valid point in the generic case. |
Hey Zarghol (JIRA User) and snoozemoose (JIRA User), I've come up with an alternative solution using the new Swift 5.1 This allows you to use the dictionary more directly without having to manually deal with forwarding function calls and member lookups. |
Comment by Mattias Persson (JIRA) @jarrodldavis Your solution works really good, thanks for this. I had to do a variant to support optional dictionaries too but other than that this is the cleanest solution yet. |
This should be also applicable to |
Yet another workaround for this issue: https://github.com/YOCKOW/SwiftCodableDictionary I realized that |
Comment by Pheki (JIRA) Note that this not only happens with Enums, but with other integer primitives, such as UInt8 and Int32, which otherwise act as Int for Codable. This is annoying as I need to implement Codable by hand for each of those structs or use the propertyWrapper dictionary instead... |
Maps with enum keys encode into an array of alternating keys and values, instead of a dictionary. And vice versa, JSON dictionary fails to be decoded into such a map, instead expects an array. See swiftlang/swift-corelibs-foundation#3690
Maps with enum keys encode into an array of alternating keys and values, instead of a dictionary. And vice versa, JSON dictionary fails to be decoded into such a map, instead expects an array. See swiftlang/swift-corelibs-foundation#3690
Additional Detail from JIRA
md5: fed4eeeb518f5736bc295db089a6027c
Issue Description:
description
When using an enum value that is RawRepresentable with String or Int, the json encoded is an an array of alternating keys and values, Instead of a Dictionary.
This bug is first discussed here : https://forums.swift.org/t/json-encoding-decoding-weird-encoding-of-dictionary-with-enum-values
repro
reproduced in Swift 4.1, (not tested in Swift 4.2)
expected
Encode and Decode a Dictionary with the enum value rawValue as the key.
actual
gives the following errors for the decoding part
give the following result for the encoding part
The text was updated successfully, but these errors were encountered: