美文网首页数据解析
Json转模型1--SwiftyJson

Json转模型1--SwiftyJson

作者: JoeXP | 来源:发表于2017-04-28 15:09 被阅读5508次

    JSON是移动端开发常用的应用层数据交换协议。最常见的场景便是,客户端向服务端发起网络请求,服务端返回JSON文本,然后客户端解析这个JSON文本,再把对应数据展现到页面上。
    但在编程的时候,处理JSON是一件麻烦事。在不引入任何轮子的情况下,我们通常需要先把JSON转为Dictionary,然后还要记住每个数据对应的Key,用这个Key在Dictionary中取出对应的Value来使用。这个过程我们会犯各种错误:

    * Key拼写错了
    * 路径写错了
    * 类型搞错了
    * 没拿到值懵逼了
    * 某一天和服务端约定的某个字段变更了,没能更新所有用到它的地方
    * ...
    

    为了解决这些问题,很多处理JSON的开源库应运而生。在Swift中,这些开源库主要朝着两个方向努力:

    1. 保持JSON语义,直接解析JSON,但通过封装使调用方式更优雅、更安全
    2. 预定义Model类,将JSON反序列化为类实例,再使用这些实例
    

    对于1,使用最广、评价最好的库非 SwiftyJSON 莫属,它很能代表这个方向的核心。它本质上仍然是根据JSON结构去取值,使用起来顺手、清晰。但也正因如此,这种做法没能妥善解决上述的几个问题,因为Key、路径、类型仍然需要开发者去指定;

    对于2,我个人觉得这是更合理的方式。由于Model类的存在,JSON的解析和使用都受到了定义的约束,只要客户端和服务端约定好了这个Model类,客户端定义后,在业务中使用数据时就可以享受到语法检查、属性预览、属性补全等好处,而且一旦数据定义变更,编译器会强制所有用到的地方都改过来才能编译通过,非常安全。这个方向上,开源库们做的工作,主要就是把JSON文本反序列化到Model类上了。这一类JSON库有 ObjectMapper、JSONNeverDie、HandyJSON 等。

    今天我们先来看看SwiftyJSON的用法。

    通常我们拿到数据会进行非常麻烦的optinonal(可选类型)进行拆包(Wrapping )操作,
    SwiftyJson内部会自动对optional进行拆包,大大简化了代码,解析非常方便,拿到的json数据data直接扔进去

    //转成JSON对象
    let jsonData = JSON.init(data)
    

    不管需要什么数据只要通过对jsonData进行路径读取
    例如:

    let build_name = jsonData[0]["build_name"].stringValue
    

    值得一提的是,不需要考虑服务器给我们返回的是什么类型,比如去请求一个房屋的栋数"build_num": 588我们想要获得Int类型或者String类型都可以

    let room_num1 = jsonData[0]["build_num"].stringValue
    let room_num2 = jsonData[0]["build_num"].intValue
    // room_num1 = 588   String类型
    // room_num2 = 588   Int类型
    

    这样通过.stringValue .intValue 就可以获得不可选值类型,如果没有获取到数据的话就会返回一个默认值即.stringValue获得空字符串"".intValue得到0.arrayValue获得空数组[],我们就不用判断拆包了。

    当然某些场景可能需要你得到可选值类型并自己判断是否存在,那么我们可以通过.string .int .bool .float .array .dictionary等等方法获取,例如:

    //String
    if let build_name = jsonData[0]["build_name"].string {
        print(build_name)
    } else {
        //打印错误信息
        print(jsonData[0]["build_name"])
    }
    

    解析数据是真的非常简单。详细使用可以看看SwiftyJSON的使用详解(附样例)
    或者移步GitHub-SwiftyJson
    但是对于项目而言我们需要对数据进行转换为模型,仅仅是对data进行JSON读取是远远不够的,想想,如果遇到许多地方都用到了build_name值,但是当服务器给我们返回的字段名字改了,我们改项目时就会显得麻烦,甚至造成改不完全,所以我们队数据封装一下转为模型,这样修改时只用改一个地方就是model的赋值就好了。

    那我的方法是:

    //这是模拟数据
    let baseInfo: [String : Any] = ["build_name":"置信·原墅",
                                      "build_address":"学院中路与金桥路交汇处东北侧",
                                      "build_num": 12,
                                      "room_num": 588,
                                      "area_address":"浙江省温州市鹿城区五马街道"]
    

    创建struct模型,当然Class也可以,但是如果不需要继承也不复杂推荐struct(-。-不多解释了)
    并写好创建方法

    import SwiftyJSON
    
    struct BuildBaseInfoModel {
        var build_name: String?
        var build_address: String?
        var build_num: String?
        var room_num: String?
        var area_address: String?
        
        init(jsonData: JSON) {
            build_name    = jsonData["build_name"].stringValue
            build_address = jsonData["build_address"].stringValue
            area_address  = jsonData["area_address"].stringValue
            room_num      = jsonData["room_num"].stringValue
            build_num     = jsonData["build_num"].stringValue
        }
    }
    

    调用:

    let jsonData = JSON(baseInfo)
    let model = BuildBaseInfoModel.init(jsonData: jsonData)
    // 会得到创建好的 BuildBaseInfoModel类型的 对象model
    // 也可以这样写
    let model = BuildBaseInfoModel(jsonData: jsonData)
    

    当然这只是普通的模型,经常会遇到复杂模型,例如:

    // 面积中89是Int, 109和129是String
    let baseInfo: [String : Any] = ["build_name":"置信·原墅",
                                      "build_address":"学院中路与金桥路交汇处东北侧",
                                      "area_address":"浙江省温州市鹿城区五马街道",
                                      "area":[89,"109","129"],
                                      "detail_address":["province":"浙江省",
                                                        "city":"温州市",
                                                        "district":"鹿城区",
                                                        "street":"五马街道"],
                                      "build_num": 12,
                                      "room_num": 588]
    

    这时候模型就应该有两个

    struct BuildBaseInfoModel {
        
        var build_name: String?
        var build_address: String?
        var build_num: String?
        var room_num: String?
        var area_address: String?
        var detail_address: DetailAddressModel
        var area:[Any]?
        // 这里面积area中就不能再用arrayValue获取了,因为arrayValue获取的为JSON类型,我们需要转为我们需要的对象
        init(jsonData: JSON) {
            build_name    = jsonData["build_name"].stringValue
            build_address = jsonData["build_address"].stringValue
            area_address  = jsonData["area_address"].stringValue
            room_num      = jsonData["room_num"].stringValue
            build_num     = jsonData["build_num"].stringValue
            area          = jsonData["area"].arrayObject
            detail_address = DetailAddressModel(jsonData: jsonData["detail_address"])
        }
    }
    struct DetailAddressModel {
        var province: String?
        var city: String?
        var district: String?
        var street: String?
        
        init(jsonData: JSON) {
            province = jsonData["province"].stringValue
            city = jsonData["city"].stringValue
            district = jsonData["district"].stringValue
            street = jsonData["street"].stringValue
        }
        
    }
    
    let model = BuildBaseInfoModel(jsonData: jsonData)
    DPrint(message: model.detail_address.city)
    DPrint(message: model.area?.first)
    DPrint(message: model.area?[1])
    // xxxxxxxxx.swift[21], updateRoomsData(index:): Optional("温州市")
    // xxxxxxxxx.swift[22], updateRoomsData(index:): Optional(89)
    // xxxxxxxxx.swift[22], updateRoomsData(index:): Optional("109")
    

    好了,完成,虽然解析Json数据非常方便,但是创建模型的时候,还是略显繁琐。不过不管怎么说目的达到了

    相关文章

      网友评论

      • 一骑红尘NO:你好,这个BuildBaseInfoModel模型的部分我有个疑问,
        所有的实例变量都是可选类型,例如var build_name: String?,
        但是你在json转的时候,用的是:build_name = jsonData["build_name"].stringValue
        .stringValue返回的不是非可选类型吗,如果你声明实例变量是可选类型,为什么不用.string,而要用.stringvalue呢.
        谢谢
      • 小源源源:至少帮助了我,支持
      • 挪威的森林_4173:json 有嵌套数组的情况 该怎么装模型 求解!
        sky_性本善:@挪威的森林_4173 非常感谢,你的指点 ,我后来 改为handyJson了,import UIKit
        import HandyJSON

        enum Grade: Int,HandyJSONEnum {
        case One = 1
        case Two = 2
        case Three = 3
        }

        enum Gender: String,HandyJSONEnum {
        case Male = "Male"
        case Female = "Female"
        }
        struct Teacher: HandyJSON {
        var name: String?
        var age: Int?
        var height: Int?
        var gender: Gender?

        }
        struct Subject: HandyJSON {
        var name: String?
        var id: String?
        var credit: Int?
        var lessonPeriod: Int?
        }

        class Student: HandyJSON {
        var id: String?
        var name: String?
        var age: Int?
        var grade: Grade = .One
        var height: Int?
        var gender: Gender?
        var className: String?
        var teacher: Teacher = Teacher()
        var subjects: [Subject]?
        var seat: String?
        required init(){}
        }
        class YGHandyJsonTest: NSObject {
        let jsonStr = "{\"id\":\"77544\",\"json_name\":\"Tom Li\",\"age\":18,\"grade\":2,\"height\":180,\"gender\":\"Female\",\"className\":\"A\",\"teacher\":{\"name\":\"Lucy He\",\"age\":28,\"height\":172,\"gender\":\"Female\",},\"subjects\":[{\"name\":\"math\",\"id\":18000324583,\"credit\":4,\"lessonPeriod\":48},{\"name\":\"computer\",\"id\":18000324584,\"credit\":8,\"lessonPeriod\":64}],\"seat\":\"4-3-23\"}"
        func jsonToModel() {
        if let student = Student.deserialize(from: jsonStr){
        print(student.toJSON())
        }
        }

        }
        挪威的森林_4173:@tao1990vi我是这么解决的
        struct XXXModel{
        var teams: Array<TeamModel>
        init(jsonData: JSON) {
        teams = []
        for json in jsonData["teams"].arrayValue {
        let element = TeamModel.init(jsonData: json)
        teams.append(element)
        }
        }
        }

        struct TeamModel {
        ....
        }

        有什么不对的 欢迎指正
        sky_性本善:解决了吗 求解
      • 何必轻言流年:大佬威武,一下就看懂了
      • 时间shiwo9:感觉还是HandyJSON比较好用
        小源源源:会有错误,integer类型
        时间shiwo9:@龙马longma 没有报错,我新建的项目,纯swift代码,没有问题
        Alan龙马:你好, 我导入 HandyJSON 报错,你的会么

      本文标题:Json转模型1--SwiftyJson

      本文链接:https://www.haomeiwen.com/subject/rntazttx.html