假设我有以下 struct 定义和字典:

struct Point: Codable {
    let x: Int
    let y: Int
}

let pointDictionary = [ "x": 0, "y": 1]

// Inital example was too easy so also we might have
struct Score: Codable {
    let points: Int
    let name: String
}

let scoreDictionary: [String: Any] = [
    "points": 10,
    "name": "iOSDevZone"
]

有没有一种方法,不需要往返于JSON或PLIST之间,就可以从pointDictionary填充 struct Point的实例?

What I've Tried

我已经看了苹果的文档,但真的找不到方法.

需要说明的是,我知道我可以编写一个接受字典的自定义初始化器(当我提交这个问题时,系统会匹配说明这一点的答案),但这不是我要问的问题.(在我的真实情况下,这是不现实的,这纯粹是一个示范性的例子).

我在问,给出一个[String:Any]的字典,其中键与 struct 的属性名匹配,值可以转换为属性的类型,有没有办法利用Decodable来初始化 struct ?

Why a Dictionary init is not desirable个 因为大多数回复都是:"为什么不实现一个字典初始化?"

有许多 struct 和许多属性,字典来自于处理错误的JSON(我无法控制).

推荐答案

这绝对是可能的,因为它是如何Foundation does it on Darwin:

open func decode<T : Decodable>(_ type: T.Type, from data: Data) throws -> T {
    let topLevel: Any
    do {
       topLevel = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed)
    } catch {
        throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: error))
    }

    let decoder = __JSONDecoder(referencing: topLevel, options: self.options)
    guard let value = try decoder.unbox(topLevel, as: type) else {
        throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: [], debugDescription: "The given data did not contain a top-level value."))
    }

    return value
}

您可以将任何内容传递给__JSONDecoder,它将对其进行解码.不幸的是,__JSONDecoder是私有的.但它也是开源的,所以这是可以修复的.太乏味了.

您需要复制大约1500 lines of __JSONDecoder implementation个和一些支持类型,删除__JSONDecoder前面的"Private",然后您可以添加一个可以执行您想要的扩展:

extension JSONDecoder {
    fileprivate var options: _Options {
        return _Options(dateDecodingStrategy: dateDecodingStrategy,
                        dataDecodingStrategy: dataDecodingStrategy,
                        nonConformingFloatDecodingStrategy: nonConformingFloatDecodingStrategy,
                        keyDecodingStrategy: keyDecodingStrategy,
                        userInfo: userInfo)
    }

    func decode<T : Decodable>(_ type: T.Type, from topLevel: Any) throws -> T {
        let decoder = __JSONDecoder(referencing: topLevel, options: self.options)
        guard let value = try decoder.unbox(topLevel, as: type) else {
            throw DecodingError.valueNotFound(type, DecodingError.Context(codingPath: [], debugDescription: "The given data did not contain a top-level value."))
        }

        return value
    }
}

// And then it "just works":
let score = try JSONDecoder().decode(Score.self, from: scoreDictionary)

Swift相关问答推荐

@ MainActor + Combine不一致的编译器错误

SwiftData查询按日期排序的项的属性数组

如何在HStack中均匀分布视图?

如何分解阿拉伯字母?

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

UICollectionViewCompostionalLayout Collectionview单元格宽度在重新加载数据后未正确调整大小

从`compactMap()`闭包返回`nil`未通过结果类型判断

这是 Int64 Swift Decoding 多余的吗?

SwiftUI 视图的默认成员初始化器 VS 自定义初始化器

在 Swift 中实现自定义异步序列

不要从 NumberFormatter 获取货币符号

swift中的SequenceType和CollectionType有什么区别?

如何快速判断设备方向是横向还是横向?

如何在 SwiftUI 中实现 PageView?

Xcode 6 中的嵌入式内容包含 Swift 代码构建设置有什么作用?

'#selector' 的参数不引用 '@objc' 方法、属性或初始化程序

Swift 3:小数到 Int

自定义 MKAnnotation 标注视图?

在 Swift 中以编程方式更新约束的常量属性?

为什么枚举具有计算(computed)属性但在 Swift 中没有存储属性?