【问题标题】:Convert Swift Encodable class typed as Any to dictionary将键入为 Any 的 Swift Encodable 类转换为字典
【发布时间】:2019-01-27 20:06:17
【问题描述】:

关于mypreviousquestions,我决定继承NSArrayController以实现所需的行为。

class NSPresetArrayController: NSArrayController {
    override func addObject(_ object: Any) {
        if let preset = object as? Preset {
            super.addObject(["name": preset.name, "value": preset.value])
        } else {
            super.addObject(object)
        }
    }
}

这可行,但如果我想要适用于任何Encodable 类的东西,而不仅仅是一个具有两个属性称为namevalue 的东西怎么办?

基本上,问题在于从一个类创建一个字典,其中键是属性名称,值是这些属性的值。

我试着写这样的东西:

class NSPresetArrayController: NSArrayController {
    override func addObject(_ object: Any) {
        if let encodableObject = object as? Encodable {
            let data = try! PropertyListEncoder().encode(encodableObject)
            let any = try! PropertyListSerialization.propertyList(from: data, options: [], format: nil)

            super.addObject(any)
        }
    }
}

但是,我得到一个编译错误:

Cannot invoke 'encode' with an argument list of type '(Encodable)'
1. Expected an argument list of type '(Value)'

如何解决这个问题以便编译?

【问题讨论】:

  • 这归结为将对象从Any 转换为“真实”类型......但老实说我不知道​​该怎么做。不过,我敢肯定有人会这样做。假设这是可能的。
  • 您无法解决此问题。 encode 的参数必须是具体类型,而不是协议。与 Objective-C 和 Swift 泛型广泛相关的 Cocoa 绑定不能一起工作。你必须找到一种没有绑定或没有泛型的方法。
  • 好吧,你可以静态枚举你将要编码的类型...... :barf:

标签: swift serialization codable


【解决方案1】:

问题在于protocols don't always conform to themselvesPropertyListEncoderencode(_:) 方法需要一个 Value : Encodable 参数:

func encode<Value : Encodable>(_ value: Value) throws -> Data

然而,Encodable 类型本身目前无法满足此约束(但它可能会在该语言的未来版本中实现)。

正如在链接的问答(和 also here)中所探讨的,解决此限制的一种方法是打开 Encodable 值以挖掘底层的具体类型,我们可以代替Value。我们可以通过协议扩展来做到这一点,并使用包装器类型来封装它:

extension Encodable {
  fileprivate func openedEncode(to container: inout SingleValueEncodingContainer) throws {
    try container.encode(self)
  }
}

struct AnyEncodable : Encodable {
  var value: Encodable
  init(_ value: Encodable) {
    self.value = value
  }
  func encode(to encoder: Encoder) throws {
    var container = encoder.singleValueContainer()
    try value.openedEncode(to: &container)
  }
}

应用于您的示例:

class NSPresetArrayController : NSArrayController {
  override func addObject(_ object: Any) {
    guard let object = object as? Encodable else { 
      // Not encodable, maybe do some error handling.
      return 
    }
    do {
      let encoded = try PropertyListEncoder().encode(AnyEncodable(object))
      let cocoaPropertyList = try PropertyListSerialization.propertyList(from: encoded, format: nil)
      super.addObject(cocoaPropertyList)
    } catch {
      // Couldn't encode. Do some error handling.
    }
  }
}

【讨论】:

    【解决方案2】:

    传递给encode(_:) 的值的类型必须是实现Encodable 的具体类型。这意味着您需要从您拥有的Any 中恢复对象的真实类型。为了进行强制转换,您必须具有要强制转换的静态指定类型。换句话说,你不能说object as! type(of: object);你必须说object as? MyClass(或者在一般情况下你可以说object as? T)。

    因此,我认为解决此问题的唯一方法是静态枚举您正在使用的类型,如下所示:

    import Foundation
    
    struct S : Encodable {
        let i: Int
    }
    
    struct T : Encodable {
        let f: Float
    }
    
    struct U : Encodable {
        let b: Bool
    }
    
    func plistObject(from encodable: Any) -> Any? {
        let encoded: Data?
        switch encodable {
            case let s as S:
                encoded = try? PropertyListEncoder().encode(s)
            case let t as T:
                encoded = try? PropertyListEncoder().encode(t)
            case let u as U:
                encoded = try? PropertyListEncoder().encode(u)
            default:
                encoded = nil
        }
    
        guard let data = encoded else { return nil }
    
        return try? PropertyListSerialization.propertyList(from: data,
                                                           options: [],
                                                           format: nil)
    }
    

    不用说,这很恶心。它是不灵活的、重复的样板。我不确定我是否真的可以推荐它的使用。这是对字面问题的回答,不一定是问题的解决方案。

    【讨论】:

    • 您可以使用协议扩展打开Encodable 值,以获得满足通用占位符的底层具体类型,请参阅我的答案:)
    猜你喜欢
    • 2020-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多