【KakaJSON手冊】04_JSON轉Model_04_值過濾

KakaJSON手冊的第2篇文章中提過:因爲JSON格式能表達的數據類型是比較有限的,因此服務器返回的JSON數據有時沒法自動轉換成客戶端想要的數據類型html

  • 好比客戶端想要的是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,參考第三篇文章
相關文章
相關標籤/搜索