在KakaJSON手冊的第2篇文章中提過:由於JSON格式能表達的數據類型是比較有限的,所以服務器返回的JSON數據有時無法自動轉換成客戶端想要的數據類型
- 比如客戶端想要的是Date類型,服務器返回的可能是字符串
"2018-08-08 08:08:08.888"
或者"2018/08/08 08:08:08.888"
- 像上述情況,KakaJSON內部是無法自動轉換的,但提供了值過濾機制,允許開發者對JSON值進行自定義處理
日期處理
// 這2個DateFormatter僅僅為了舉例子而寫的,具體細節根據自己需求而定
private let date1Fmt: DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd"
return fmt
}()
private let date2Fmt: DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
return fmt
}()
struct Student: Convertible {
var date1: Date?
var date2: NSDate?
// 實現kj_modelValue方法
// 會傳入屬性`property`以及這個屬性對應的JSON值`jsonValue`
// 返回值是你希望最后設置到模型屬性上的值
// 如果返回`jsonValue`,代表不做任何事,交給KakaJSON內部處理
// 如果返回`nil`,代表忽略這個屬性,KakaJSON不會給這個屬性設值(屬性會保留它的默認值)
func kj_modelValue(from jsonValue: Any?, _ property: Property) -> Any? {
switch property.name {
// 如果jsonValue是字符串,就直接轉成Date
case "date1": return (jsonValue as? String).flatMap(date1Fmt.date)
// 如果jsonValue是字符串,就直接轉成Date
// 由於NSDate與Date之間是可以橋接轉換的,所以返回Date給NSDate屬性也是沒有問題的
case "date2": return (jsonValue as? String).flatMap(date2Fmt.date)
default: return jsonValue
}
}
}
let date1 = "2008-09-09"
let date2 = "2011-11-12 14:20:30.888"
let json: [String: Any] = [
"date1": date1,
"date2": date2
]
let student = json.kj.model(Student.self)
// 將Date\NSDate轉回字符串進行比較
XCTAssert(student.date1.flatMap(date1Fmt.string) == date1)
XCTAssert(student.date2.flatMap(date2Fmt.string) == date2)
不確定類型
// 有時候服務器返回的某個字段的內容類型可能是不確定的
// 客戶端可以先標記為Any類型或者AnyObject類型或者協議類型等不確定類型
struct Person: Convertible {
var name: String = ""
var pet: Any?
func kj_modelValue(from jsonValue: Any?, _ property: Property) -> Any? {
// 如果不是`pet`屬性,就按照默認處理
if property.name != "pet" { return jsonValue }
// 如果是`pet`屬性,並且`jsonValue`是個字典,就轉換為`Dog`模型實例
// 具體判斷邏輯可以根據實際開發需求而定
return (jsonValue as? [String: Any])?.kj.model(Dog.self)
}
}
struct Dog: Convertible {
var name: String = ""
var weight: Double = 0.0
}
let json: [String: Any] = [
"name": "Jack",
"pet": ["name": "Wang", "weight": 109.5]
]
let person = json.kj.model(Person.self)
XCTAssert(person.name == "Jack")
let pet = person.pet as? Dog
XCTAssert(pet?.name == "Wang")
XCTAssert(pet?.weight == 109.5)
/*---------------------------------------------*/
class Book: Convertible {
var name: String = ""
var price: Double = 0.0
required init() {}
}
struct Person: Convertible {
var name: String = ""
// [AnyObject]、[Convertible]、NSArray、NSMutableArray
var books: [Any]?
func kj_modelValue(from jsonValue: Any?,
_ property: Property) -> Any? {
if property.name != "books" { return jsonValue }
// if books is `NSMutableArray`, neet convert `Array` to `NSMutableArray`
// because `Array` to `NSMutableArray` is not a bridging conversion
return (jsonValue as? [Any])?.kj.modelArray(Book.self)
}
}
let name = "Jack"
let books = [
(name: "Fast C++", price: 666),
(name: "Data Structure And Algorithm", price: 1666)
]
let json: [String: Any] = [
"name": name,
"books": [
["name": books[0].name, "price": books[0].price],
["name": books[1].name, "price": books[1].price]
]
]
let person = json.kj.model(Person.self)
XCTAssert(person.name == name)
XCTAssert(person.books?.count == books.count)
let book0 = person.books?[0] as? Book
XCTAssert(book0?.name == books[0].name)
XCTAssert(book0?.price == Double(books[0].price))
let book1 = person.books?[1] as? Book
XCTAssert(book1?.name == books[1].name)
XCTAssert(book1?.price == Double(books[1].price))
其他例子
struct Student: Convertible {
var age: Int = 0
var name: String = ""
func kj_modelValue(from jsonValue: Any?, _ property: Property) -> Any? {
switch property.name {
// 如果`age`屬性的`jsonValue`是整數,就加上5
case "age": return (jsonValue as? Int).flatMap { $0 + 5 }
// 如果`name `屬性的`jsonValue`是字符串,就在前面加上`kj_`
case "name": return (jsonValue as? String).flatMap { "kj_" + $0 }
default: return jsonValue
}
}
}
let json: [String: Any] = [
"age": 10,
"name": "Jack"
]
let student = json.kj.model(Student.self)
XCTAssert(student.age == 15)
XCTAssert(student.name == "kj_Jack")
其他實現思路
// 關於值過濾、自定義值處理的邏輯,也可以在模型轉換完畢之后進行
struct Student: Convertible {
var age: Int = 0
var name: String = ""
// 實現`kj_didConvertToModel`方法,在這里修改轉換之后的屬性值
mutating func kj_didConvertToModel(from json: [String: Any]) {
age += 5
name = "kj_" + name
}
}
let json: [String: Any] = [
"age": 10,
"name": "Jack"
]
let student = json.kj.model(Student.self)
XCTAssert(student.age == 15)
XCTAssert(student.name == "kj_Jack")
最后的提示
kj_modelValue
也支持ConvertibleConfig
配置,用法類似於kj_modelKey
,參考第三篇文章