Json 如何在Swift中对可编码词典进行编码?

Json 如何在Swift中对可编码词典进行编码?,json,swift,dictionary,encoding,codable,Json,Swift,Dictionary,Encoding,Codable,我有一个用Swift编写的[String:Codable]字典,我想将它保存到用户默认值中,但我正在努力做到这一点 我已尝试使用 try! JSONSerialization.data(withJSONObject: dictionary, options: .init(rawValue: 0)) 但这会崩溃(“JSON写入中的类型无效(_SwiftValue)”) 我试过使用jsonecoder: JSONEncoder().encode(dictionary) 但这不会编译(“无法推断通

我有一个用Swift编写的
[String:Codable]
字典,我想将它保存到用户默认值中,但我正在努力做到这一点

我已尝试使用

try! JSONSerialization.data(withJSONObject: dictionary, options: .init(rawValue: 0))
但这会崩溃(“JSON写入中的类型无效(_SwiftValue)”)

我试过使用
jsonecoder

JSONEncoder().encode(dictionary)
但这不会编译(“无法推断通用参数T”)

当然,我可以手动将我所有的Codable转换为[String:Any],然后将其写入用户默认值,但由于Codable的全部目的是使解码和编码变得容易,我不太清楚为什么上面的两个解决方案不可能(特别是第二个)

示例

对于再现性,您可以在操场上使用此代码:

import Foundation

struct A: Codable {}
struct B: Codable {}

let dict = [ "a": A(), "b": B() ] as [String : Codable]
let data = try JSONEncoder().encode(dict)

Codable
,作为一般约束,和
Any
不可编码。使用结构而不是字典:

struct A: Codable {
    let a = 0
}
struct B: Codable {
    let b = "hi"
}
struct C: Codable {
    let a: A
    let b: B
}

let d = C(a: A(), b: B())
let data = try JSONEncoder().encode(d)

UserDefaults有一种保存[String:Any]字典的方法:

let myDictionary: [String: Any] = ["a": "one", "b": 2]
UserDefaults.standard.set(myDictionary, forKey: "key")
let retrievedDictionary: [String: Any] = UserDefaults.standard.dictionary(forKey: "key")!
print(retrievedDictionary)      // prints ["a": one, "b": 2]
但是,如果词典是要保存到
UserDefaults
的对象的属性,则需要为对象实现
Codable
协议。我知道的最简单的方法是使用
JSONSerialization
将字典转换为
Data
对象。以下代码适用于我:

class MyObject: Codable {

    let dictionary: [String: Any]

    init(dictionary: [String: Any]) {
        self.dictionary = dictionary
    }

    enum CodingKeys: String, CodingKey {
        case dictionary
    }

    public required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        if values.contains(.dictionary), let jsonData = try? values.decode(Data.self, forKey: .dictionary) {
            dictionary = (try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any]) ??  [String: Any]()
        } else {
            dictionary = [String: Any]()
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        if !dictionary.isEmpty, let jsonData = try? JSONSerialization.data(withJSONObject: dictionary) {
            try container.encode(jsonData, forKey: .dictionary)
        }
    }
}
要从
UserDefaults
保存和检索
MyObject
,您可以执行以下操作:

extension UserDefaults {

    func set(_ value: MyObject, forKey defaultName: String) {
        guard let data = try? PropertyListEncoder().encode(value) else { return }
        set(data, forKey: defaultName)
    }

    func myObject(forKey defaultName: String) -> MyObject? {
        guard let data = data(forKey: defaultName) else { return nil }
        return try? PropertyListDecoder().decode(MyObject.self, from: data)
    }
}

这在操场上对我有用:
struct My:Codable{};让dict=[“你好”:我的(),“世界”:我的();让data=try JSONEncoder().encode(dict)
你能展示你的结构和所有相关的代码吗,这样它是可复制的吗?不幸的是,这不是因为它是公司代码,但我认为你的例子是可行的,因为它是一个同质的集合<代码>[String:My]起作用,但
[String:Codable]
不起作用。所以,让dict=[“hello”:My(),“world”:Yours()]无法工作。如果有必要,我可以提供一个示例。我已经在问题中添加了示例代码。您可以使用包装器类型,请参见示例(然后执行示例
jsonecoder().encode(dict.mapValues(AnyEncodable.init))
)——不过我强烈建议重新考虑是否
[String:Codable]
是能够代表您的模型的最强类型。无意冒犯,但我几乎要将此作为答案发布。我刚写完,你的一个出现了是的,我想这是有道理的:D有点遗憾,我们必须为此创建一个命名实体,但你要做什么呢。谢谢PS:抱歉@nayem:D