使用@dynamicMemberLookup访问AnyCodable值

问题描述 投票:3回答:1

Objc.io谈论轻松改变无类型词典,但问题是你不能轻易地坚持它们。我认为在@dynamicMemberLookup推出之前可能已经发布了这个话题。

AnyCodable看起来很棒,很容易编码/解码/持久化简单的词典,但你不能轻易访问词典成员。

我想知道将Swift 4.2中发现的@dynamicMemberLookup功能(例如:在this example中)添加到AnyCodable是否可行/可行,如果是这样,怎么样?最终目标是访问/改变无类型数组或字典并保留它们。

所以,我尝试这样做:

@dynamicMemberLookup
public struct AnyCodable: Codable {
    public let value: Any

    public init<T>(_ value: T?) {
        self.value = value ?? ()
    }

    subscript(dynamicMember member: String) -> AnyCodable? {
        switch self.value {
        case let dictionary as [String: Any?]:
            return AnyCodable(dictionary[member])
        default:
            return nil
        }
    }
}

给出AnyCodable的示例词典:

let dictionary: [String: AnyEncodable] = [
    "boolean": true,
    "integer": 1,
    "double": 3.14159265358979323846,
    "string": "string",
    "array": [1, 2, 3],
    "nested": [
        "a": "alpha",
        "b": "bravo",
        "c": "charlie"
    ]
]

如果我做:

if let nested = dictionary["nested"] {
    print("nested a:", nested.a)
}

它输出:nested a: Optional(AnyCodable(Optional("alpha")))几乎就在那里!但我希望能够简单地写dictionary?.nested?.adictionary?.array?[1]而不是首先用nested解开if let nested = dictionary["nested"]。而且我希望能够改变它,例如:dictionary?.nested?.a? = "beta"

我无法弄清楚如何通过终点线获得它。我显然需要添加case let array as [Any]:等,并可能更改下标以包括getter / setter?但我还缺少什么呢?

我知道你可能“不应该以这种方式使用字典”并创建一个完整的自定义类型模型和所有这些,但这是一个小项目,走这条路线将是矫枉过正。所以请不要回答“以不同方式对数据建模”。我想将这两种现有的访问/持久化非字典字典或数组的方法合并为一种。

swift persistence codable swift4.2 untyped-variables
1个回答
0
投票

好的,我想我的主要内容是它。

第一个问题是,你使用字典。您只能将@dynamicMemberLookup添加到主定义,因此您无法在字典定义上执行此操作。试试这个:

let dictionary: [String: AnyEncodable] = [ ... ]
let easierToUse = AnyCodable(dictionary)

所以考虑下面的代码,你需要的是什么? :

let dictionary: [String: AnyCodable] = [
    "boolean": true,
    "integer": 1,
    "double": 3.14159265358979323846,
    "string": "string",
    "array": [1, 2, 3],
    "nested": [
        "a": "alpha",
        "b": "bravo",
        "c": "charlie",
        "array": [
            1,
            2,
            [
                "a": "alpha",
                "b": "bravo",
                "c": "deep charlie"
            ]
        ],
    ]
]
let easierToUse: AnyCodable = AnyCodable(dictionary)

if let value = easierToUse.nested?.a {
    print(value) // prints "alpha"
}

if let value = easierToUse.nested?.array?[2]?.c {
    print(value) // prints "deep charlie"
}

if let value = easierToUse.nested?.array?[2]?.c?.value as? String {
    print(value) // prints "deep charlie"
}

我不得不更新你的课程,因为你忘记了它们都包含在每个级别:

// Helper to handle out of bounds on array with nil
extension Array {
    subscript (safe index: Int) -> Element? {
        return indices ~= index ? self[index] : nil
    }
}

@dynamicMemberLookup
public struct AnyCodable: Codable {
    public let value: Any

    public init<T>(_ value: T) {
        self.value = value
    }

    public init<T>(_ value: T?) {
        self.value = value ?? ()
    }

    subscript(dynamicMember member: String) -> AnyCodable? {
        switch self.value {
        case let anyCodable as AnyCodable:
            return anyCodable[dynamicMember: member]
        case let dictionary as [String: Any?]:
            return AnyCodable(dictionary[member] ?? nil)
        default:
            return nil
        }
    }

    subscript(index: Int) -> AnyCodable? {
        switch self.value {
        case let anyCodable as AnyCodable:
            return anyCodable[index]
        case let array as [Any]:
            return AnyCodable(array[safe: index])
        default:
            return nil
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.