Swift 使用@dynamicMemberLookup访问任意可编码值

Swift 使用@dynamicMemberLookup访问任意可编码值,swift,persistence,codable,swift4.2,untyped-variables,Swift,Persistence,Codable,Swift4.2,Untyped Variables,关于易变异的非类型化词典有一个很好的讨论,但问题是你不能轻易地将它们持久化。我想这次演讲可能是在@dynamicMemberLookup推出之前发布的 简单字典的编码/解码/持久化看起来很棒,但您无法轻松访问字典成员 我想知道是否有可能/可行将Swift 4.2(例如:in)中的@dynamicMemberLookup功能添加到任何可编码表中,如果有,如何添加最终目标是访问/变异非类型化数组或字典并将其持久化。 所以,我试着这样做: @dynamicMemberLookup public str

关于易变异的非类型化词典有一个很好的讨论,但问题是你不能轻易地将它们持久化。我想这次演讲可能是在
@dynamicMemberLookup
推出之前发布的

简单字典的编码/解码/持久化看起来很棒,但您无法轻松访问字典成员

我想知道是否有可能/可行将Swift 4.2(例如:in)中的
@dynamicMemberLookup
功能添加到任何可编码表中,如果有,如何添加最终目标是访问/变异非类型化数组或字典并将其持久化。

所以,我试着这样做:

@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
        }
    }
}
如果我这样做:

if let nested = dictionary["nested"] {
    print("nested a:", nested.a)
}
它输出:
嵌套的a:Optional(AnyCodable(Optional)(“alpha”))
几乎就在那里了!但我希望能够简单地编写
dictionary?.nested?.a
dictionary?.array?[1]
,而不是先用
打开
nested
,如果让nested=dictionary[“nested”]
。我希望能够对它进行变异,例如:
dictionary?.nested?.a?=“测试版”

不过,我不知道怎样才能越过终点线。我显然需要将
case-let数组添加为[Any]:
等,并且可能会更改下标以包含getter/setter?但是我还缺少什么呢


我知道你可能“不应该以这种方式使用字典”,并创建一个完整的自定义类型模型等等,但这是一个小项目,在这个项目中,走这条路线会有点过头。因此,请不要回答“以不同的方式对数据建模”。我想将这两种现有的访问/持久化非类型化词典或数组的方法结合到一起。

好的,我想我已经大致介绍过了

第一个问题是,你使用字典。您只能将@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
        }
    }
}
//帮助器在具有nil的数组上处理越界
扩展阵列{
下标(安全索引:Int)->元素{
回报指数~=指数?自[指数]:零
}
}
@动态成员查找
公共结构AnyCodable:Codable{
公共出租价值:任何
公共初始化(u值:T){
自我价值=价值
}
公共初始化(u值:T?){
self.value=值??()
}
下标(dynamicMember成员:String)->AnyCodable{
转换自我价值{
case let anyCodable作为anyCodable:
返回anyCodable[dynamicMember:member]
case let dictionary as[字符串:有吗?]:
返回AnyCodable(字典[成员]??nil)
违约:
归零
}
}
下标(索引:Int)->AnyCodable{
转换自我价值{
case let anyCodable作为anyCodable:
返回anyCodable[索引]
case let数组作为[任何]:
返回AnyCodable(数组[安全:索引])
违约:
归零
}
}
}

太棒了,谢谢!这似乎确实管用!布景怎么样?例如:
easierouse.nested?.array?[2]?.c=“not charlie”
为了在dynamicMemberLookup子脚本中使用
set{…}
,整个设置是否需要是一个类而不是一个结构?因为
AnyCodable
成员是值引用的吗也许你可以保持它的结构,我今天会尝试玩变异的东西。这是一个有点难,因为对于你已经做了大部分时间的获得者work@taber它变得有点太大了,无法粘贴到响应中。我分叉了原始回购协议,并添加了所有dynamicMemberLookup内容。添加了获取/设置值的测试,您可以在:啊,很好!也许我用错了,但是如果我更改并尝试保存示例dict,我会得到一个
AnyCodable值cannot encoded
错误。您是否能够保存并加载键入为AnyCodable的示例dict?我的测试代码:
var easierToUse:AnyCodable=[…这里的示例dict…]easierToUse.nested?.array?[2]?.c=“NOT charles”do{let data=try jsonecoder().encode(easierToUse)try data.write(to:fileUrl)}catch let error{print(error)}
如果我不更改它,它会保存罚款。(难道任何编码都不应该是可编码和可解码的吗?)
// 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
        }
    }
}