【问题标题】:Can Swift convert a class / struct data into dictionary?Swift 可以将类/结构数据转换为字典吗?
【发布时间】:2017-10-06 02:59:44
【问题描述】:

例如:

class Test {
    var name: String;
    var age: Int;
    var height: Double;
    func convertToDict() -> [String: AnyObject] { ..... }
}

let test = Test();
test.name = "Alex";
test.age = 30;
test.height = 170;

let dict = test.convertToDict();

dict 会有内容:

{"name": "Alex", "age": 30, height: 170}

这在 Swift 中可行吗?

我可以访问像字典这样的类吗,例如可能使用:

test.value(forKey: "name");

或者类似的?

谢谢。

【问题讨论】:

  • 为什么要这样做?您可以直接在类/结构上使用关键路径。那么这里有什么意义呢?字典以何种方式比类/结构“更好”? “我可以像字典一样访问类吗?”是的,确切地说,在 Swift 4 中使用键路径。
  • 您要转换的目的是什么?如果您要保存数据,您应该创建一个带有类的模型。将 struct 转换为 Data 会更麻烦。
  • 我想在 ViewController 之间进行 JSON 编码和传递值的转换。从接受的答案来看,我相信Encodable 是关键。

标签: swift class object dictionary


【解决方案1】:

您只需将计算属性添加到您的 struct 即可返回带有您的值的 Dictionary。请注意,Swift 本机字典类型没有任何称为 value(forKey:) 的方法。您需要将您的 Dictionary 转换为 NSDictionary

struct Test {
    let name: String
    let age: Int
    let height: Double
    var dictionary: [String: Any] {
        return ["name": name,
                "age": age,
                "height": height]
    }
    var nsDictionary: NSDictionary {
        return dictionary as NSDictionary
    }
}

您还可以按照@ColGraff 发布的链接答案中的建议扩展Encodable 协议,使其适用于所有Encodable 结构:

struct JSON {
    static let encoder = JSONEncoder()
}
extension Encodable {
    subscript(key: String) -> Any? {
        return dictionary[key]
    }
    var dictionary: [String: Any] {
        return (try? JSONSerialization.jsonObject(with: JSON.encoder.encode(self))) as? [String: Any] ?? [:]
    }
}

struct Test: Codable {
    let name: String
    let age: Int
    let height: Double
}

let test = Test(name: "Alex", age: 30, height: 170)
test["name"]    // Alex
test["age"]     // 30
test["height"]  // 170

【讨论】:

  • 实际上,我使用示例value(forKey 只是为了说明我需要什么,即通过使用变量键而不是硬编码的键来查询对象以获取其属性的值。我不打算使用NSDictionary。我计划将它与 JSON 结合使用,所以我相信您的 Encodable 协议最适合我的问题。我不知道搜索此类问题的关键字,但现在我知道了。我知道我可以创建一个个性化的函数来为每个类返回自定义字典,我只是想知道是否有一个通用的方法可以做到这一点。谢谢!!
  • Encodable 扩展方法很优雅!谢谢狮子座。
【解决方案2】:

您可以像这样使用 Reflection 和 Mirror 来使其更具动态性并确保您不会忘记某个属性。

struct Person {
  var name:String
  var position:Int
  var good : Bool
  var car : String

  var asDictionary : [String:Any] {
    let mirror = Mirror(reflecting: self)
    let dict = Dictionary(uniqueKeysWithValues: mirror.children.lazy.map({ (label:String?, value:Any) -> (String, Any)? in
      guard let label = label else { return nil }
      return (label, value)
    }).compactMap { $0 })
    return dict
  }
}


let p1 = Person(name: "Ryan", position: 2, good : true, car:"Ford")
print(p1.asDictionary)

["name": "Ryan", "position": 2, "good": true, "car": "Ford"]

【讨论】:

  • 当有嵌套结构时它是如何工作的?我的意思是让我们说另一个结构体 Company 具有名称、位置、id 等字段,并且结构体 Person 有一个财产公司:Company。那么这个镜像逻辑是如何工作的呢?
  • 有没有办法从列表中删除一些项目-> key: optional("value")?
  • 删除选项
【解决方案3】:

使用协议,这是一个优雅的解决方案。
1. 将结构或类编码为数据
2。解码数据并传输到字典。

/// define protocol convert Struct or Class to Dictionary
protocol Convertable: Codable {

}

extension Convertable {

    /// implement convert Struct or Class to Dictionary
    func convertToDict() -> Dictionary<String, Any>? {

        var dict: Dictionary<String, Any>? = nil

        do {
            print("init student")
            let encoder = JSONEncoder()

            let data = try encoder.encode(self)
            print("struct convert to data")

            dict = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? Dictionary<String, Any>

        } catch {
            print(error)
        }

        return dict
    }
}

struct Student: Convertable {

    var name: String
    var age: Int
    var classRoom: String

    init(_ name: String, age: Int, classRoom: String) {
        self.name = name
        self.age = age
        self.classRoom = classRoom
    }
}

let student = Student("zgpeace", age: 18, classRoom: "class one")

print(student.convertToDict() ?? "nil")

参考:https://a1049145827.github.io/2018/03/02/Swift-%E4%BB%8E%E9%9B%B6%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AAStruct%E6%88%96Class%E8%BD%ACDictionary%E7%9A%84%E9%9C%80%E6%B1%82/

【讨论】:

  • 谢谢。这对我有用。
【解决方案4】:

聚会有点晚了,但我认为这是JSONEncoderJSONSerialization 的绝佳机会。 接受的答案确实涉及到这一点,这个解决方案让我们每次访问密钥时都调用JSONSerialization,但同样的想法!

extension Encodable {

    /// Encode into JSON and return `Data`
    func jsonData() throws -> Data {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        encoder.dateEncodingStrategy = .iso8601
        return try encoder.encode(self)
    }
}

如果Encodable 应表示为 JSON 中的对象(例如 Swift Array 将是 JSON 数组),则您可以使用 JSONSerialization 创建 Dictionary

这是一个例子:

struct Car: Encodable {
    var name: String
    var numberOfDoors: Int
    var cost: Double
    var isCompanyCar: Bool
    var datePurchased: Date
    var ownerName: String? // Optional
}

let car = Car(
    name: "Mazda 2",
    numberOfDoors: 5,
    cost: 1234.56,
    isCompanyCar: true,
    datePurchased: Date(),
    ownerName: nil
)

let jsonData = try car.jsonData()

// To get dictionary from `Data`
let json = try JSONSerialization.jsonObject(with: jsonData, options: [])
guard let dictionary = json as? [String : Any] else {
    return
}

// Use dictionary

guard let jsonString = String(data: jsonData, encoding: .utf8) else {
    return
}

// Print jsonString
print(jsonString)

输出:

{
  "numberOfDoors" : 5,
  "datePurchased" : "2020-03-04T16:04:13Z",
  "name" : "Mazda 2",
  "cost" : 1234.5599999999999,
  "isCompanyCar" : true
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-21
    • 2018-02-25
    • 2017-02-24
    相关资源
    最近更新 更多