Fork me on GitHub

Swift 从零实现一个Struct或Class转Dictionary的需求

今天在逛知乎的时候无意间看到这样一个提问 swift中struct怎么转成字典?,心想应该不难实现,正好需要写点Swift练练手,于是乎开始搞起。

首先想到的是将Struct(遵守Codable协议)用JSONEncoder的encode转成Data,然后再用JSONSerialization反序列化成Dictionary对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct Student: Codable {

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("xiaoming", age: 10, classRoom: "五一班")

do {
print("init student")
let encoder = JSONEncoder()
let data = try encoder.encode(student)
print("struct convert to data")

let dict = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(type(of: dict))
print(dict)

} catch {
print(error)
}

在回答完这个问题之后,我又想了一下,这要是每次转换一下,都这么写一遍,那不就疯了吗?

一不做,二不休。继续写代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func convertStructToDict<T: Generator where T.Element:
Codable>(structObj: T) -> Dictionary<String, Any>? {

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

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

if let obj = structObj as? Decodable {

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

dict = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(type(of: dict))
print(dict ?? "")
}

} catch {
print(error)
}
}

这次的思路是,封装一个方法,来实现 传入一个遵守Codable协议的Struct或Class实例,经过函数内部处理,返回一个转换好的Dictionary实例出来。

结果编译器报错:

1
2
3
error: MyPlayground.playground:260:39: error: 'where' clause next to generic parameters is obsoleted
func convertStructToDict<T: Generator where T.Element:
~^~~~~~~~~~~~~~~~

正一筹莫展之时,心想这不能在一棵树上吊死,试试别的方法,说不定可以通过协议扩展的方法来实现。但又不知道协议是否支持继承,经过查找得到了肯定的答案:

Swift中协议允许继承

图片出处链接:Swift中的Protocol知道这些就够了

看到这里,心中立马有了方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/// 直接将Struct或Class转成Dictionary
protocol Convertable: Codable {

}

extension Convertable {

/// 直接将Struct或Class转成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("xiaoming", age: 10, classRoom: "五一班")

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

现在只需要新建一个Struct或者Class,然后遵守Convertable协议,这个类或者结构体就获得了一行代码转换成Dictionary的能力,十分的方便,而且使用起来也能确保类型安全。

附上我整理好的代码: github链接

------------- 本文结束感谢您的阅读 -------------