我的数据 struct 有一个enum作为密钥,我希望下面的代码能自动解码.这是一个错误还是一个配置问题?

import Foundation

enum AnEnum: String, Codable {
  case enumValue
}

struct AStruct: Codable {
  let dictionary: [AnEnum: String]
}

let jsonDict = ["dictionary": ["enumValue": "someString"]]
let data = try! JSONSerialization.data(withJSONObject: jsonDict,     options: .prettyPrinted)
let decoder = JSONDecoder()
do {
  try decoder.decode(AStruct.self, from: data)
} catch {
  print(error)
}

我得到的错误是,似乎把dict和数组混淆了.

类型不匹配(Swift.Array,Swift.DecodingError.Context(codingPath:

推荐答案

问题是Dictionary's Codable conformance目前只能正确处理StringInt个键.对于任何其他Key类型(其中KeyEncodable/Decodable)的字典,它是用一个unkeyed容器(JSON数组)编码和解码的,该容器具有交替的键值.

因此,在try 解码JSON时:

{"dictionary": {"enumValue": "someString"}}

AStruct中,"dictionary"键的值应该是一个array.

所以

let jsonDict = ["dictionary": ["enumValue", "someString"]]

会起作用,产生JSON:

{"dictionary": ["enumValue", "someString"]}

然后会被解码成:

AStruct(dictionary: [AnEnum.enumValue: "someString"])

然而,我真的认为DictionaryCodable一致性should能够正确地处理任何CodingKey一致性类型,就像它的Key(AnEnum可以)一样——因为它可以用该密钥编码和解码到一个密钥容器中(file a bug可以随意请求).

在实现之前(如果有的话),我们始终可以构建一个包装器类型来实现这一点:

struct CodableDictionary<Key : Hashable, Value : Codable> : Codable where Key : CodingKey {

    let decoded: [Key: Value]

    init(_ decoded: [Key: Value]) {
        self.decoded = decoded
    }

    init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: Key.self)

        decoded = Dictionary(uniqueKeysWithValues:
            try container.allKeys.lazy.map {
                (key: $0, value: try container.decode(Value.self, forKey: $0))
            }
        )
    }

    func encode(to encoder: Encoder) throws {

        var container = encoder.container(keyedBy: Key.self)

        for (key, value) in decoded {
            try container.encode(value, forKey: key)
        }
    }
}

然后像这样实施:

enum AnEnum : String, CodingKey {
    case enumValue
}

struct AStruct: Codable {

    let dictionary: [AnEnum: String]

    private enum CodingKeys : CodingKey {
        case dictionary
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        dictionary = try container.decode(CodableDictionary.self, forKey: .dictionary).decoded
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(CodableDictionary(dictionary), forKey: .dictionary)
    }
}

(或者只需使用CodableDictionary<AnEnum, String>类型的dictionary属性,并使用自动生成的Codable一致性——然后只需使用dictionary.decoded)

现在我们可以按预期解码嵌套的JSON对象:

let data = """
{"dictionary": {"enumValue": "someString"}}
""".data(using: .utf8)!

let decoder = JSONDecoder()
do {
    let result = try decoder.decode(AStruct.self, from: data)
    print(result)
} catch {
    print(error)
}

// AStruct(dictionary: [AnEnum.enumValue: "someString"])

尽管如此,但可以说,用enum作为键的字典所实现的只是一个具有可选属性的struct(如果你希望给定的值总是存在,就让它成为非可选的).

因此,你可能只想让你的模型看起来像:

struct BStruct : Codable {
    var enumValue: String?
}

struct AStruct: Codable {

    private enum CodingKeys : String, CodingKey {
        case bStruct = "dictionary"
    }

    let bStruct: BStruct
}

使用当前的JSON就可以了:

let data = """
{"dictionary": {"enumValue": "someString"}}
""".data(using: .utf8)!

let decoder = JSONDecoder()
do {
    let result = try decoder.decode(AStruct.self, from: data)
    print(result)
} catch {
    print(error)
}

// AStruct(bStruct: BStruct(enumValue: Optional("someString")))

Swift相关问答推荐

如何在SWIFT中轻松格式化公制和英制使用的UnitVolume

如何获取SCNCamera正在查看的网格点(SCNNode)的局部坐标和局部法线?

在visionOS 1.0中已弃用';base Color';:请改用`Color`属性

我正在try 通过代码设置节中页眉和页脚的高度,但它不起作用

我需要一些关于 SwiftUI 动画的帮助

OSX 中的 Popover 无法确定透明度

减小SF符号的边框宽度

为什么我的Swift app不能用xcodebuild构建,但是用XCode可以构建?

在 Swift 5.7 中使用协议作为类型时什么时候需要写 `any`

Swift // Sprite Kit:类别位掩码限制

如何在一个视图控制器中创建具有相同行为的多个集合视图?

无法增加系统镜像的大小

如何将回调传递给 View init

如何删除桥头而不出错?

iOS 判断应用程序是否可以访问麦克风

为 UIActivityViewController Swift 设置不同的活动项

在 Swift 中将计时器标签格式化为小时:分钟:秒

如何在 Swift 中遍历 struct 属性?

Swift 中的 CommonHMAC

ISO8601DateFormatter 不解析 ISO 日期字符串