自從開始使用Swift做項目,一直都在使用HandyJSON,不可否認,HandyJSON在Swift4.0以前是個好東西,也嘗試過其它json轉mode的工具,最終發現還是HandyJSON最好用. 去年Swift4.0發布之后,一個最有趣的變化就是Codable協議. 一直都知道Codable來實現json轉model,不但效率高,並且簡單易用, 但是一直拖到最近才簡單封裝個小工具,為什么呢? 懶😂!!!
在工具的封裝上,參考了HandyJSON的部分代碼,在使用HandyJSON過程中,一直都覺得designatedPath
是一個很牛逼的存在,開發效率提升了不是一個量級,於是這里也參考HandyJSON中designatedPath的實現代碼,並根據Codable的需要改造成fileprivate func getInnerObject(inside jsonData: Data?, by designatedPath: String?) -> Data?
方法. 在這里感謝HandyJSON開發組.
Codable
擴展全部代碼如下所示.100余行的代碼前后花費一天多的時間. 當了解到google工程師日均一百多行的代碼量,我覺得這速度還可以吧. 在代碼質量和閱讀質量上不敢說有多好,我只是按照自己認為最好的方式來做,如果有哪里不當或者可以用更好方法解決的地方,煩請各位告知,彼此交流學習.
Codable
協議擴展實現代碼如下所示:
//
// CodableHelper.swift
// CodableDemo
//
// Created by Walden on 2018/5/7.
// Copyright © 2018年 Walden. All rights reserved.
//
import Foundation
//擴展Encodable協議,添加編碼的方法
public extension Encodable {
//1.遵守Codable協議的對象轉json字符串
public func toJSONString() -> String? {
guard let data = try? JSONEncoder().encode(self) else {
return nil
}
return String(data: data, encoding: .utf8)
}
//2.對象轉換成jsonObject
public func toJSONObject() -> Any? {
guard let data = try? JSONEncoder().encode(self) else {
return nil
}
return try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
}
}
//擴展Decodable協議,添加解碼的方法
public extension Decodable {
//3.json字符串轉對象&數組
public static func decodeJSON(from string: String?, designatedPath: String? = nil) -> Self? {
guard let data = string?.data(using: .utf8),
let jsonData = getInnerObject(inside: data, by: designatedPath) else {
return nil
}
return try? JSONDecoder().decode(Self.self, from: jsonData)
}
//4.jsonObject轉換對象或者數組
public static func decodeJSON(from jsonObject: Any?, designatedPath: String? = nil) -> Self? {
guard let jsonObject = jsonObject,
JSONSerialization.isValidJSONObject(jsonObject),
let data = try? JSONSerialization.data(withJSONObject: jsonObject, options: []),
let jsonData = getInnerObject(inside: data, by: designatedPath) else {
return nil
}
return try? JSONDecoder().decode(Self.self, from: jsonData)
}
}
//擴展Array,添加將jsonString或者jsonObject解碼到對應對象數組的方法
public extension Array where Element: Codable {
public static func decodeJSON(from jsonString: String?, designatedPath: String? = nil) -> [Element?]? {
guard let data = jsonString?.data(using: .utf8),
let jsonData = getInnerObject(inside: data, by: designatedPath),
let jsonObject = try? JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) as? [Any] else {
return nil
}
return Array.decodeJSON(from: jsonObject)
}
public static func decodeJSON(from array: [Any]?) -> [Element?]? {
return array?.map({ (item) -> Element? in
return Element.decodeJSON(from: item)
})
}
}
/// 借鑒HandyJSON中方法,根據designatedPath獲取object中數據
///
/// - Parameters:
/// - jsonData: json data
/// - designatedPath: 獲取json object中指定路徑
/// - Returns: 可能是json object
fileprivate func getInnerObject(inside jsonData: Data?, by designatedPath: String?) -> Data? {
//保證jsonData不為空,designatedPath有效
guard let _jsonData = jsonData,
let paths = designatedPath?.components(separatedBy: "."),
paths.count > 0 else {
return jsonData
}
//從jsonObject中取出designatedPath指定的jsonObject
let jsonObject = try? JSONSerialization.jsonObject(with: _jsonData, options: .allowFragments)
var result: Any? = jsonObject
var abort = false
var next = jsonObject as? [String: Any]
paths.forEach({ (seg) in
if seg.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) == "" || abort {
return
}
if let _next = next?[seg] {
result = _next
next = _next as? [String: Any]
} else {
abort = true
}
})
//判斷條件保證返回正確結果,保證沒有流產,保證jsonObject轉換成了Data類型
guard abort == false,
let resultJsonObject = result,
let data = try? JSONSerialization.data(withJSONObject: resultJsonObject, options: []) else {
return nil
}
return data
}
CodableHelper
工具的使用也是非常簡單的,代碼如下所示:
//首先定義一個結構體Person用來表示數據Model
struct Person: Codable {
var name: String?
var age: Int?
var sex: String?
}
//1.jsonString中獲取數據封裝成Model
let p1String = "{\"name\":\"walden\",\"age\":30,\"sex\":\"man\"}"
let p1 = Person.decodeJSON(from: p1String)
//2.jsonString中獲取數據封裝成Array
let personString = "{\"haha\":[{\"name\":\"walden\",\"age\":30,\"sex\":\"man\"},{\"name\":\"healer\",\"age\":20,\"sex\":\"female\"}]}"
let persons = [Person].decodeJSON(from: personString, designatedPath: "haha")
//3.對象轉jsonString
let jsonString = p1?.toJSONString()
//4.對象轉jsonObject
let jsonObject = p1?.toJSONObject()
目前存在問題:
- Model中定義的數據類型和jsonString中數據類型不對應時候會導致解析失敗;
- 轉載請注明出處