Issue #854
If you use enum case as key in Dictionary, JSONEncoder will encode it as Array. For example
enum Vehicle: String, Codable {
case car
case truck
}
struct Container: Codable {
var map: [Vehicle: String]
}
struct Container2: Codable {
var map: [String: String]
}
let container = Container(map: [
.car: "Car 1"
])
let container2 = Container2(map: [
"car": "Car 1"
])
let data = try! JSONEncoder().encode(container)
print(String(data: data, encoding: .utf8)) // {"map":["car","Car 1"]}
let data2 = try! JSONEncoder().encode(container2)
print(String(data: data2, encoding: .utf8)) // {"map":{"car":"Car 1"}}
The reason is how JSONEncoder checks for type https://github.com/apple/swift/blob/d2085d8b0e/stdlib/public/core/Codable.swift.gyb#L1967
If the dictionary uses
String
orInt
keys, the contents are encoded in a keyed container. Otherwise, the contents are encoded as alternating key-value pairs in an unkeyed container.
extension Dictionary : Encodable where Key : Encodable, Value : Encodable {
/// Encodes the contents of this dictionary into the given encoder.
///
/// If the dictionary uses `String` or `Int` keys, the contents are encoded
/// in a keyed container. Otherwise, the contents are encoded as alternating
/// key-value pairs in an unkeyed container.
///
/// This function throws an error if any values are invalid for the given
/// encoder's format.
///
/// - Parameter encoder: The encoder to write data to.
@inlinable // FIXME(sil-serialize-all)
public func encode(to encoder: Encoder) throws {
if Key.self == String.self {
// Since the keys are already Strings, we can use them as keys directly.
var container = encoder.container(keyedBy: _DictionaryCodingKey.self)
for (key, value) in self {
let codingKey = _DictionaryCodingKey(stringValue: key as! String)!
try container.encode(value, forKey: codingKey)
}
} else if Key.self == Int.self {
// Since the keys are already Ints, we can use them as keys directly.
var container = encoder.container(keyedBy: _DictionaryCodingKey.self)
for (key, value) in self {
let codingKey = _DictionaryCodingKey(intValue: key as! Int)!
try container.encode(value, forKey: codingKey)
}
} else {
// Keys are Encodable but not Strings or Ints, so we cannot arbitrarily
// convert to keys. We can encode as an array of alternating key-value
// pairs, though.
var container = encoder.unkeyedContainer()
for (key, value) in self {
try container.encode(key)
try container.encode(value)
}
}
}
}