Swift 4 Decodable-以枚举为键的字典 [英] Swift 4 Decodable - Dictionary with enum as key

查看:221
本文介绍了Swift 4 Decodable-以枚举为键的字典的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的数据结构有一个枚举作为键,我希望下面的内容能自动解码。这是错误还是某些配置问题?

My data structure has an enum as a key, I would expect the below to decode automatically. Is this a bug or some configuration issue?

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)
}

我得到的错误是


typeMismatch(Swift.Array,Swift.DecodingError.Context(codingPath:
[可选(__lldb_expr_85.AStruct。(
_0E2FD0A9B523101D0DCD67578F72D1DD中的CodingKeys.dictionary)],debugDescription:预期对数组进行解码,但找到了字典。))

typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [Optional(__lldb_expr_85.AStruct.(CodingKeys in _0E2FD0A9B523101D0DCD67578F72D1DD).dictionary)], debugDescription: "Expected to decode Array but found a dictionary instead."))


推荐答案

问题是 字典 Codable 符合性可以目前只能正确处理 String Int 键。对于具有任何其他 Key 类型的字典(其中 Key Encodable / 可解码),它使用带有交替键值的 unkeyed 容器(JSON数组)进行编码和解码。

The problem is that Dictionary's Codable conformance can currently only properly handle String and Int keys. For a dictionary with any other Key type (where that Key is Encodable/Decodable), it is encoded and decoded with an unkeyed container (JSON array) with alternating key values.

因此,在尝试解码JSON时:

Therefore when attempting to decode the JSON:

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

放入 AStruct 字典 键的值应该是一个数组。

into AStruct, the value for the "dictionary" key is expected to be an array.

因此,

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

可以工作,并生成JSON:

would work, yielding the JSON:

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

然后将其解码为:

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

但是,我真的认为 Dictionary Co dable 一致性应该能够正确处理任何 CodingKey 一致性类型作为其 Key AnEnum 可以)-因为它可以使用该密钥进行编码和解码到带密钥的容器中(可以自由使用提交错误以请求此操作。)

However, really I think that Dictionary's Codable conformance should be able to properly deal with any CodingKey conforming type as its Key (which AnEnum can be) – as it can just encode and decode into a keyed container with that key (feel free to file a bug requesting for this).

直到实施(如果有的话),我们总是可以构建一个包装器类型来做到这一点:

Until implemented (if at all), we could always build a wrapper type to do this:

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> 的code> dictionary 属性,并使用自动生成的 Codable 一致性–然后只需按照 dictionary.decoded

(or just have the dictionary property of type CodableDictionary<AnEnum, String> and use the auto-generated Codable conformance – then just speak in terms of dictionary.decoded)

来进行,按预期嵌套JSON对象:

Now we can decode the nested JSON object as expected:

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 (如果您希望给定值总是


Although that all being said, it could be argued that all you're achieving with a dictionary with an enum as a key is just a struct with optional properties (and if you expect a given value to always be there; make it non-optional).

因此,您可能只希望模型看起来像这样:

Therefore you may just want your model to look like:

struct BStruct : Codable {
    var enumValue: String?
}

struct AStruct: Codable {

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

    let bStruct: BStruct
}

哪个会很好用使用您当前的JSON:

Which would work just fine with your current 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 4 Decodable-以枚举为键的字典的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆