[英]Can Swift convert a class / struct data into dictionary?
For example:例如:
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 will have content: dict 将包含以下内容:
{"name": "Alex", "age": 30, height: 170}
Is this possible in Swift?这在 Swift 中可能吗?
And can I access a class like a dictionary, for example probably using:我可以访问像字典这样的类吗,例如可能使用:
test.value(forKey: "name");
Or something like that?或类似的东西?
You can just add a computed property to your struct
to return a Dictionary
with your values.您只需将一个计算属性添加到您的
struct
中,即可返回带有您的值的Dictionary
。 Note that Swift native dictionary type doesn't have any method called value(forKey:)
.请注意,Swift 本机字典类型没有任何称为
value(forKey:)
的方法。 You would need to cast your Dictionary
to NSDictionary
:您需要将
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
}
}
You can also extend Encodable
protocol as suggested at the linked answer posted by @ColGraff to make it universal to all Encodable
structs:您还可以按照@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
You could use Reflection and Mirror like this to make it more dynamic and ensure you do not forget a property.您可以像这样使用 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"] [“名称”:“瑞恩”,“位置”:2,“好”:真,“汽车”:“福特”]
A bit late to the party, but I think this is great opportunity for JSONEncoder
and JSONSerialization
.聚会有点晚了,但我认为这是
JSONEncoder
和JSONSerialization
的绝佳机会。 The accepted answer does touch on this, this solution saves us calling JSONSerialization
every time we access a key, but same idea!接受的答案确实涉及到这一点,这个解决方案可以节省我们每次访问密钥时调用
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)
}
}
You can then use JSONSerialization
to create a Dictionary
if the Encodable
should be represented as an object in JSON (eg Swift Array
would be a JSON array)然后,如果
Encodable
应表示为 JSON 中的对象(例如 Swift Array
将是 JSON 数组),则可以使用JSONSerialization
创建Dictionary
Here's an example:这是一个例子:
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)
Output:输出:
{
"numberOfDoors" : 5,
"datePurchased" : "2020-03-04T16:04:13Z",
"name" : "Mazda 2",
"cost" : 1234.5599999999999,
"isCompanyCar" : true
}
Use protocol, it is an elegant solution.使用协议,这是一个优雅的解决方案。
1. encode struct or class to data 1. 将结构或类编码为数据
2. decode data and transfer to dictionary. 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")
ref: 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/参考: 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/
This answer is like the above which uses Mirror.这个答案就像上面使用 Mirror 的答案。 But consider the nested class/struct case.
但是考虑嵌套类/结构的情况。
extension Encodable {
func dictionary() -> [String:Any] {
var dict = [String:Any]()
let mirror = Mirror(reflecting: self)
for child in mirror.children {
guard let key = child.label else { continue }
let childMirror = Mirror(reflecting: child.value)
switch childMirror.displayStyle {
case .struct, .class:
let childDict = (child.value as! Encodable).dictionary()
dict[key] = childDict
case .collection:
let childArray = (child.value as! [Encodable]).map({ $0.dictionary() })
dict[key] = childArray
case .set:
let childArray = (child.value as! Set<AnyHashable>).map({ ($0 as! Encodable).dictionary() })
dict[key] = childArray
default:
dict[key] = child.value
}
}
return dict
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.