Swift可以将类/结构数据转换为字典吗?

Che*_*ong 14 dictionary class object swift

例如:

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();
Run Code Online (Sandbox Code Playgroud)

dict将有内容:

{"name": "Alex", "age": 30, height: 170}
Run Code Online (Sandbox Code Playgroud)

这在Swift中可能吗?

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

test.value(forKey: "name");
Run Code Online (Sandbox Code Playgroud)

或类似的东西?

谢谢.

Leo*_*bus 36

您只需向您的计算属性添加struct一个Dictionary带有值的返回值即可.请注意,Swift本机字典类型没有任何调用方法value(forKey:).您需要将您投射DictionaryNSDictionary:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

您还可以Encodable按照@ColGraff发布的链接答案的建议扩展协议,使其适用于所有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] ?? [:]
    }
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

  • Encodable 扩展方法很优雅!谢谢狮子座。 (4认同)

Rya*_*ner 9

您可以像这样使用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 label != nil else { return nil }
      return (label!,value)
    }).compactMap{ $0 })
    return dict
  }
}


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

[“名称”:“ Ryan”,“位置”:2,“良好”:真实,“汽车”:“福特”]

  • 当存在嵌套结构时它如何工作?我的意思是,假设有另一个 struct Company,其中包含名称、位置、id 等字段,而 struct Person 有一个财产公司:Company。那么这个镜像逻辑是如何运作的呢? (2认同)

Bec*_*hsh 9

聚会有点晚了,但我认为这对JSONEncoder和来说是一个很好的机会JSONSerialization。接受的答案确实涉及到这一点,这个解决方案让我们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)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用JSONSerialization创建一个DictionaryifEncodable应该表示为 JSON 中的对象(例如 SwiftArray将是一个 JSON 数组)。

这是一个例子:

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
)

// Get the encoded JSON data
let jsonData = try car.jsonData()
    
// To get a dictionary from the JSON data
let json = try JSONSerialization.jsonObject(with: jsonData, options: [])
guard let dictionary = json as? [String : Any] else { return }
    
// Use dictionary...

// Print jsonString
guard let jsonString = String(data: jsonData, encoding: .utf8) else { return }
print(jsonString)
Run Code Online (Sandbox Code Playgroud)

输出:

{
  "numberOfDoors" : 5,
  "datePurchased" : "2020-03-04T16:04:13Z",
  "name" : "Mazda 2",
  "cost" : 1234.5599999999999,
  "isCompanyCar" : true
}
Run Code Online (Sandbox Code Playgroud)