在iOS開發中,後臺返回的數據大可能是JSON格式,對應地會被網絡框架層解析成Swift中的Dictionary、Array。因爲數據類型的複雜、字段的繁多,直接使用Dictionary、Array會比較麻煩,好比items[0]["user"]["name"]這樣的使用方式,很是不友善,並且沒有智能語法提示。因此不少時候會考慮將JSON轉換成Model以後再進行操做,會友善不少,好比items[0].user.name。html
import KakaJSON // ① 讓模型類型遵照`Convertible`協議 struct Cat: Convertible { var name: String = "" var weight: Double = 0.0 } // json也能夠是NSDictionary、NSMutableDictionary類型 let json: [String: Any] = [ "name": "Miaomiao", "weight": 6.66 ] // ② 直接調用json的model方法,傳入模型類型,返回模型實例 let cat1 = json.kj.model(Cat.self) XCTAssert(cat1.name == "Miaomiao") XCTAssert(cat1.weight == 6.66) // 或者也能夠調用一個全局函數來完成JSON轉模型 let cat2 = model(from: json, Cat.self)
// 有時類型多是個變量,好比 var type: Convertible.Type = Cat.self // 調用帶有type參數的方法便可 // 因爲傳入的類型是Convertible.Type變量,所以返回值類型是Convertible,到時根據需求強制轉換成本身想要的類型 let cat1 = json.kj.model(type: type) as? Cat // 或者調用全局函數 let cat2 = model(from: json, type: type) as? Cat
class Cat: Convertible { var weight: Double = 0.0 var name: String = "" // 因爲Swift初始化機制的緣由,`Convertible`協議強制要求實現init初始化器 // 這樣框架內部才能夠完整初始化一個實例 required init() {} } let json = ... let cat = json.kj.model(Cat.self) // 繼承自NSObject的類也是同樣的用法 class Person: NSObject, Convertible { var name: String = "" var age: Int = 0 // 因爲NSObject內部已經有init,所以Person算是重載init,需再加上`override` required override init() {} } let person = json.kj.model(Person.self) struct Dog: Convertible { var weight: Double = 0.0 var name: String = "" // 因爲編譯器自動幫結構體類型生成了一個init初始化器 // 因此不須要本身再實現init初始化器 } struct Pig: Convertible { var weight: Double var name: String // 若是沒有在定義屬性的同時指定初始值,編譯器是不會爲結構體生成init初始化器的 // 因此須要本身實現init初始化器 init() { name = "" weight = 0.0 } }
// 有繼承的狀況也是照常使用便可 class Person: Convertible { var name: String = "" var age: Int = 0 required init() {} } class Student: Person { var score: Int = 0 var no: String = "" } let json: [String: Any] = [ "name": "jack", "age": 18, "score": 98, "no": "9527" ] let student = json.kj.model(Student.self)
// KakaJSON也支持let屬性 struct Cat: Convertible { // 測試代表:在真機release模式下,對數字類型的let限制比較嚴格 // 值雖然修改爲功了(能夠打印Cat結構體發現weight已經改掉了),但get出來仍是0.0 // 因此建議使用`private(set) var`取代`let` private(set) var weight: Double = 0.0 let name: String = "" } let json = ... let cat = json.kj.model(Cat.self)
struct Cat: Convertible { var weight: Double = 0.0 var name: String = "xx" var data: NSNull? } let json: [String: Any] = [ "name": NSNull(), "weight": 6.6, "data": NSNull() ] let cat = json.kj.model(Cat.self) // 轉換失敗,保留默認值 XCTAssert(cat.name == "xx") XCTAssert(cat.weight == 6.6) XCTAssert(cat.data == NSNull())
// jsonString也能夠是NSString、NSMutableString類型 let jsonString = """ { "name": "Miaomiao", "weight": 6.66 } """ // 跟JSON的用法是同樣的 let cat1 = jsonString.kj.model(Cat.self) let cat2 = model(from: jsonString, Cat.self) var type: Convertible.Type = Cat.self let cat3 = jsonString.kj.model(type: type) as? Cat let cat4 = model(from: jsonString, type: type) as? Cat
// jsonData也能夠是NSData、NSMutableData類型 let jsonData = """ { "name": "Miaomiao", "weight": 6.66 } """.data(using: .utf8)! // 跟JSON的用法是同樣的 let cat1 = jsonData.kj.model(Cat.self) let cat2 = model(from:jsonData, Cat.self) var type: Convertible.Type = Cat.self let cat3 = jsonData.kj.model(type: type) as? Cat let cat4 = model(from: jsonData, type: type) as? Cat
// 讓須要進行轉換的模型都遵照`Convertible`協議 struct Book: Convertible { var name: String = "" var price: Double = 0.0 } struct Car: Convertible { var name: String = "" var price: Double = 0.0 } struct Dog: Convertible { var name: String = "" var age: Int = 0 } struct Person: Convertible { var name: String = "" var car: Car? var books: [Book]? var dogs: [String: Dog]? } let json: [String: Any] = [ "name": "Jack", "car": ["name": "BMW7", "price": 105.5], "books": [ ["name": "Fast C++", "price": 666.6], ["name": "Data Structure And Algorithm", "price": 1666.6] ], "dogs": [ "dog0": ["name": "Larry", "age": 5], "dog1": ["name": "ErHa", "age": 2] ] ] // 也是如此簡單,不用再作額外的操做 let person = json.kj.model(Person.self) XCTAssert(person.car?.name == "BMW7") XCTAssert(person.books?[1].name == "Data Structure And Algorithm") XCTAssert(person.dogs?["dog0"]?.name == "Larry")
// Set也能像Array那樣支持Model嵌套 // Set要求存放的元素遵照Hashable協議 struct Book: Convertible, Hashable { var name: String = "" var price: Double = 0.0 } struct Person: Convertible { var name: String = "" var books: Set<Book>? } let json: [String: Any] = [ "name": "Jack", "books": [ ["name": "Fast C++", "price": 666.6] ] ] let person = json.kj.model(Person.self) XCTAssert(person.name == "Jack") XCTAssert(person.books?.count == 1) // 從Set中取出來是個Book模型 let book = person.books?.randomElement() XCTAssert(book?.name == "Fast C++") XCTAssert(book?.price == 666.6)
struct Car: Convertible { var name: String = "" var price: Double = 0.0 } class Dog: Convertible { var name: String = "" var age: Int = 0 required init() {} init(name: String, age: Int) { self.name = name self.age = age } } struct Person: Convertible { var name: String = "" // 若是你的模型有默認值,KakaJSON內部不會再建立新的模型 // 會直接重複利用你建立的模型,節省內存分配和初始化的開銷 var car: Car = Car(name: "Bently", price: 106.5) var dog: Dog = Dog(name: "Larry", age: 5) } let json: [String: Any] = [ "name": "Jake", "car": ["price": 305.6], "dog": ["name": "Wangwang"] ] let person = json.kj.model(Person.self) XCTAssert(person.name == "Jake") // 保留默認值 XCTAssert(person.car.name == "Bently") // 從json解析過來的值 XCTAssert(person.car.price == 305.6) // 從json解析過來的值 XCTAssert(person.dog.name == "Wangwang") // 保留默認值 XCTAssert(person.dog.age == 5)
class Person: Convertible { var name: String = "" var parent: Person? required init() {} } let json: [String: Any] = [ "name": "Jack", "parent": ["name": "Jim"] ] let person = json.kj.model(Person.self) XCTAssert(person.name == "Jack") XCTAssert(person.parent?.name == "Jim")
struct NetResponse<Element>: Convertible { let data: Element? = nil let msg: String = "" private(set) var code: Int = 0 } struct User: Convertible { let id: String = "" let nickName: String = "" } struct Goods: Convertible { private(set) var price: CGFloat = 0.0 let name: String = "" } let json1 = """ { "data": {"nickName": "KaKa", "id": 213234234}, "msg": "Success", "code" : 200 } """ let response1 = json1.kj.model(NetResponse<User>.self) XCTAssert(response1?.msg == "Success") XCTAssert(response1?.code == 200) XCTAssert(response1?.data?.nickName == "KaKa") XCTAssert(response1?.data?.id == "213234234") let json2 = """ { "data": [ {"price": "6199", "name": "iPhone XR"}, {"price": "8199", "name": "iPhone XS"}, {"price": "9099", "name": "iPhone Max"} ], "msg": "Success", "code" : 200 } """ let response2 = json2.kj.model(NetResponse<[Goods]>.self) XCTAssert(response2?.msg == "Success") XCTAssert(response2?.code == 200) XCTAssert(response2?.data?.count == 3) XCTAssert(response2?.data?[0].price == 6199) XCTAssert(response2?.data?[0].name == "iPhone XR") XCTAssert(response2?.data?[1].price == 8199) XCTAssert(response2?.data?[1].name == "iPhone XS") XCTAssert(response2?.data?[2].price == 9099) XCTAssert(response2?.data?[2].name == "iPhone Max")
struct Car: Convertible { var name: String = "" var price: Double = 0.0 } // json數組能夠是Array<[String: Any]>、NSArray、NSMutableArray let json: [[String: Any]] = [ ["name": "Benz", "price": 98.6], ["name": "Bently", "price": 305.7], ["name": "Audi", "price": 64.7] ] // 調用json數組的modelArray方法便可 let cars = json.kj.modelArray(Car.self) XCTAssert(cars[1].name == "Bently") // 一樣的還有其餘方式 let cars2 = modelArray(from: json, Car.self) var type: Convertible.Type = Car.self let cars3 = json.kj.modelArray(type: type) as? [Car] let cars4 = modelArray(from: json, type: type) as? [Car] // 另外,jsonString轉爲Model數組,也是如此簡單 let jsonString = "...." let cars5 = jsonString.kj.modelArray(Car.self) let cars6 = modelArray(from: jsonString, Car.self) let cars7 = jsonString.kj.modelArray(type: type) as? [Car] let cars8 = modelArray(from: jsonString, type: type) as? [Car]
// 若是你想把JSON數據轉換到本來已經建立好的模型實例上,可使用convert方法 struct Cat: Convertible { var name: String = "" var weight: Double = 0.0 } let json: [String: Any] = [ "name": "Miaomiao", "weight": 6.66 ] var cat = Cat() // .kj_m是.kj的mutable版本,牽扯到修改實例自己都是.kj_m開頭 cat.kj_m.convert(json) XCTAssert(cat.name == "Miaomiao" XCTAssert(cat.weight == 6.66)
// 有時候可能想在JSON轉模型以前、以後作一些額外的操做 // KakaJSON會在JSON轉模型以前調用模型的kj_willConvertToModel方法 // KakaJSON會在JSON轉模型以後調用模型的kj_didConvertToModel方法 struct Car: Convertible { var name: String = "" var age: Int = 0 mutating func kj_willConvertToModel(from json: [String: Any]) { print("Car - kj_willConvertToModel") } mutating func kj_didConvertToModel(from json: [String: Any]) { print("Car - kj_didConvertToModel") } } let name = "Benz" let age = 100 let car = ["name": name, "age": age].kj.model(Car.self) // Car - kj_willConvertToModel // Car - kj_didConvertToModel XCTAssert(car.name == name) XCTAssert(car.age == age) /*************************************************************/ // 一樣也支持類 class Person: Convertible { var name: String = "" var age: Int = 0 required init() {} func kj_willConvertToModel(from json: [String: Any]) { print("Person - kj_willConvertToModel") } func kj_didConvertToModel(from json: [String: Any]) { print("Person - kj_didConvertToModel") } } class Student: Person { var score: Int = 0 override func kj_willConvertToModel(from json: [String: Any]) { // 若是有必要的話,能夠調用super的實現 super.kj_willConvertToModel(from: json) print("Student - kj_willConvertToModel") } override func kj_didConvertToModel(from json: [String: Any]) { // 若是有必要的話,能夠調用super的實現 super.kj_didConvertToModel(from: json) print("Student - kj_didConvertToModel") } } let name = "jack" let age = 10 let score = 100 let student = ["name": name, "age": age, "score": score].kj.model(Student.self) // Person - kj_willConvertToModel // Student - kj_willConvertToModel // Person - kj_didConvertToModel // Student - kj_didConvertToModel XCTAssert(student.name == name) XCTAssert(student.age == age) XCTAssert(student.score == score)