###### tags: `第14屆IT邦鐵人賽文章`
# 【在 iOS 開發路上的大小事2-Day03】工人智慧!JSON 轉起來
最近專案需要有轉換 JSON 的功能,所以就去研究一下了
Swift 原生就有 JSON 編碼、解碼的功能
只要讓 class/struct 繼承 Encodable 或是 Decodable 就可以有編碼 或是 解碼的功能
如果是繼承 Codable 的話,則是 Encodable 跟 Decodable 這兩種功能都有
![](https://i.imgur.com/grtfKFg.png)
▲圖截自 Xcode 內的 Swift.Misc
那接下來就來開始製作要做成 JSON 的 class
```swift
// 先寫沒繼承 Codable 的 class
class Event: NSObject {
var Index: Int?
var TimeStamp: Int64?
var EventID: Int?
var EventValue: Int?
var EventAttribute: [String]?
}
```
↑ 分別寫兩個來做個對比 ↓
```swift
// 在寫有繼承 Codable 的 class
class Event: NSObject, Codable {
var Index: Int?
var TimeStamp: Int64?
var EventID: Int?
var EventValue: Int?
var EventAttribute: [String]?
}
```
接著先來寫一下要製作成 JSON 的內容
```swift
let event = Event()
event.Index = 0
event.TimeStamp = 1648647707
event.EventID = 2
event.EventValue = 0
event.EventAttribute = ["饅頭", "1", "早餐早餐"]
```
接著再來寫轉換 func~
```swift
/// 轉換 API 上傳的 JSON Format
/// - Parameters:
/// - input: EventTable
/// - Returns: JSON Data
func GetJsonEventReport(input: Event) -> Data? {
}
```
這邊有兩種版本的寫法,一種是對 Encodable 進行 extension,一種是直接寫
```swift
// Version 1:對 Encodable 進行 extension
extension Encodable {
func asDictionary() throws -> [String: Any] {
let data = try JSONEncoder().encode(self)
guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) as? [String: Any] else {
throw NSError()
}
return dictionary
}
}
func GetJsonEventReport(input: Event) -> Data? {
let dic1 = try? input.asDictionary()
print("dic1:\n \(dic1)\n")
let jsonResults = try? JSONSerialization.data(withJSONObject: dic1 ?? [:], options: .prettyPrinted)
return jsonResults
}
```
```
Version 1 輸出結果:
dic1:
Optional(["EventID": 2, "EventValue": 0, "Index": 0, "TimeStamp": 1648647707, "EventAttribute": <__NSArrayI 0x600002036af0>(
饅頭,
1,
早餐早餐
)
])
```
```swift
// Version 2:直接寫
func GetJsonEventReport(input: Event) -> Data? {
let dic2: [String: Any] = [
"CmdType": 2,
"ID": input.Index,
"DeviceID": "SK0xE0512444378B",
"UserID": "leo160918@gmail.com",
"EventID": input.EventID,
"EventValue": input.EventValue,
"EventRecordTime": input.TimeStamp?.timeStampToDate().convert2UtcStr(),
"EventAttribute": input.EventAttribute,
"Note": input.EventAttribute?.last
]
print("dic2:\n \(dic2)\n")
let jsonResults = try? JSONSerialization.data(withJSONObject: dic2 ?? [:], options: .prettyPrinted)
return jsonResults
}
```
```
Version 2 輸出結果:
dic2:
["CmdType": 2, "EventRecordTime": Optional("30/03/2022T13:41:47"), "ID": Optional(0), "EventValue": Optional(0), "DeviceID": "SK0xE0512444378B", "Note": Optional("早餐早餐"), "EventID": Optional(2), "EventAttribute": Optional(["饅頭", "1", "早餐早餐"])]
```
這兩種版本的寫法都各有優缺點~
下面是我想到的啦,可能還有其他的?
```
Version 1 優點:
● 可以不用手打,用 Code 直接生成 Dictionary
● 承上點,不會有打錯 Key 的問題
Version 1 缺點:
● 彈性比較低
● 承上點,如果有要新增其他 Key 的話,要手動新增
● 承上點,如果要將自動生成的 Dictionary 裡面特定的 Key 更換的話,要先將原先的 Key 移除
Version 2 優點:
● 彈性比較高
● 承上點,如果有要新增其他 Key 的話,可以直接新增
Version 2 缺點:
● 用手打 Key,可能會有打錯 Key 的問題
● Code 不夠簡潔,要逐一給值
```
然後補充一下 JSONSerialization 這個東東
![](https://i.imgur.com/yxLOVDt.png)
▲圖截自 Apple Developer Documentation
一般來說只有 NSArray、NSDictionary 這兩種資料結構可以轉成 JSON
但如果設成 **fragmentsAllowed** (前身是 **allowFragments**) 的話,就允許將 String、Number、null、Bool 轉成 JSON
像是下面這樣 ↓
```swift
guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) as? [String: Any] else {
throw NSError()
}
```
另外將輸出格式設為 **prettyPrinted** 是為了美化格式
像是下面這樣 ↓
```swift
let jsonResults = try? JSONSerialization.data(withJSONObject: dic2 ?? [:], options: .prettyPrinted)
```
如果要看輸出的 JSON 檔的話,可以透過下面的方法
```swift
// jsonResults 是透過 Version 2 的 func GetJsonEventReport(input: Event) -> Data? 回傳出來的 Data
if let jsonData = jsonResults, let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let pathWithFileName = documentDirectory.appendingPathComponent("Event_JSON")
do {
try jsonData.write(to: pathWithFileName)
} catch {
// handle error
}
}
```
輸出結果如下~
```json
{
"EventRecordTime" : "30\/03\/2022T13:41:47",
"ID" : 0,
"DeviceID" : "SK0xE0512444378B",
"EventValue" : 0,
"EventAttribute" : [
"饅頭",
"1",
"早餐早餐"
],
"Note" : "早餐早餐",
"EventID" : 2,
"CmdType" : 2
}
```
## 參考資料
> 1. https://yongpenglovemimi123.gitbook.io/henry/ios/jsonserialization.readingoptions
> 2. https://yongpenglovemimi123.gitbook.io/henry/ios/jsonserialization.writingoptions
> 3. https://jjeremy-xue.medium.com/swift-%E7%96%91%E5%95%8F-jsonserialization-%E7%9A%84-options-d151a1e66266
> 4. https://www.codeleading.com/article/5134462945/
> 5. https://developer.apple.com/documentation/foundation/jsonserialization
> 6. https://ithelp.ithome.com.tw/articles/10245672