将类型为Any的Swift Encodable类转换为字典 [英] Convert Swift Encodable class typed as Any to dictionary

查看:658
本文介绍了将类型为Any的Swift Encodable类转换为字典的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上一个 问题,我决定将<$ c $子类化c> NSArrayController 以实现所需的行为。

In connection with my previous questions, I decided to subclass NSArrayController in order to achieve the desired behavior.

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 类,而不仅仅是具有两个属性的一个,这些属性分别称为 name value

This works, but what if I wanted something that works for any Encodable class, and not just one with two properties called name and value?

基本上,问题是从类创建字典,其中键是属性名,值是这些属性的值

Basically, the problem is creating a dictionary from a class, where the keys are the property names, and the values are the values of these properties.

我尝试编写如下代码:

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)'

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

How do I fix this so it compiles?

推荐答案

问题在于,协议并不总是符合自己的要求 PropertyListEncoder encode(_:) 方法期望 Value:可编码参数:

The problem is that protocols don't always conform to themselves. PropertyListEncoder's encode(_:) method expects a Value : Encodable argument:

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

但是 Encodable 类型本身目前是

如链接的问答A中所述(和也在这里),一种变通方法此限制是为了 open 打开 Encodable 值,以便挖掘底层的具体类型,我们可以用它代替 Value 。我们可以使用协议扩展来做到这一点,并使用包装器类型对其进行封装:

As explored in the linked Q&A (and also here), one way to work around this limitation is to open the Encodable value in order to dig out the underlying concrete type, which we can substitute for Value. We can do this with a protocol extension, and use a wrapper type in order to encapsulate it:

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.
    }
  }
}

这篇关于将类型为Any的Swift Encodable类转换为字典的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆