zoukankan      html  css  js  c++  java
  • 【KakaJSON手册】04_JSON转Model_04_值过滤

    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)
    // 将DateNSDate转回字符串进行比较
    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,参考第三篇文章
  • 相关阅读:
    闲扯 Javascript 01 实现选项卡
    控制台获得键盘事件
    C#反射 入门学习 02
    C#反射 入门学习 01
    闲扯 Javascript 00
    读张子阳老师的委托和事件 2
    浅析ado.net获取数据库元数据信息 DeriveParameters
    SQLBulkCopy使用
    利用CryptoStream进行加密解密
    vs 中代码的字体也颜色设置
  • 原文地址:https://www.cnblogs.com/mjios/p/11365528.html
Copyright © 2011-2022 走看看