在iOS中解析JSON

2 篇文章 0 订阅

相对于冗长的XML, JSON格式的文件现在应用得愈加广泛。JSON虽然看似简单,但有些小陷阱须得注意。再加上解析时,必须双向考虑JSON文件与代码之间的衔接,这使得在iOS中解析JSON具有一定的难度。本文通过使用Swift语言,分4个步骤,从最简单的例子开始,最后讨论解决较为棘手的数组问题,在编码过程中具有一定的意义。


## 最简单的JSON解析


设有一个名为*test.json*的文件,其内容如下:


    {

        "name": "Mike"

    }

    

又有一个名为Person的struct:


    struct Person: Decodable {

        var name: String

    }


则可如下解析:


    var url = Bundle.main.url(forResource: "test", withExtension: "json")

    

    do {

        let data = try Data(contentsOf: url!)

        let decoder = JSONDecoder()

        let person = try decoder.decode(Person.self, from: data)


        print(person.name)

    } catch {

        print("error: \(error.localizedDescription)")

    }


则打印出:


    Mike


上面须满足这几个条件:

1. Person必须具备Decodable的protocol;

2. 因为*test.json*有一个*name*的属性,相对应地,Person也须有一个名为*name*的变量。


此外,*test.json*必须是一个有效的JSON格式。比较容易犯的错误是:


    {

        name: "Mike"

    }


由于没有将*name*以双引号括起来,导致无效的JSON格式,解析时就会出错。


*decode<T>(T.Type, from: Data)*方法返回一个类型的实例,在上面的例子中是*Person*的实例。


小结: **凡是在JSON中出现"{}"的地方,应在代码中为其设立一个类或结构。同时将JSON中的键设为类或结构的变量。**


## 解析多个属性


*test.json*增加一个*sex*的属性。


    {

        "name": "Mike",

        "sex": "male"

    }


则Person也应增加一个*sex*的变量。


    struct Person: Decodable {

        var name: String

        var sex: String

    }


注意,在*test.json*中,多个并列的属性,须以逗号","隔开,否则要出错。


## 解析嵌套的属性


下面增加电话属性。因为可能拥有多个电话,因此,有必要分别增加*home**cellPhone*的属性。


    {

        "name": "Mike",

        "sex": "male",

        "phones": {

            "home": "66308273",

            "cellPhone": "13907567320"

        }

    }


*home**cellPhone**phones*的嵌套属性。在代码中,可以将*phones*当作一个类或结构,因此,与前相似,需为其新定义一个结构。


    struct Phones: Decodable {

        var home: String

        var cellPhone: String

    }


有了这个结构,Swift就能自动将嵌套的属性对应到*Phones*结构中。


当然,应为*Person*加上*phones*这个新的变量:


    struct Person: Decodable {

        var name: String

        var sex: String

        var phones: Phones

    }


## 解析数组


上面已经成功地解析了一个Person. 下面看看如何同时解析多个Persons.


先修改*test.json*文件。


    [

        "name": "Mike",

        "sex": "male",

        "phones": {

            "home": "66308273",

            "cellPhone": "13907567320"

        },

     

        "name": "Alice",

        "sex": "female",

        "phones": {

            "home": "68557026",

            "cellPhone": "13693528325"

        }

    ]


首先,最外层的符号改为中括号,以表示数组,且第一个人与第二个人的信息之间用“,”相隔开。


这样的JSON格式对吗?


不对,虽然使用了中括号表示数组,虽然两个人之间以一个空行隔开,但在JSON中,这不是两个对象的组合,而是具有重复键的无效格式。让我们简化一下,只保留*name*的属性来看看。


    [

        "name": "Mike",

        "name": "Alice"

    ]


这是两个具有相同键的组合,而不是两个对象的组合。对于对象的组合,每个对象均应以"{}"包围起来。


    [

        {"name": "Mike"},

        {"name": "Alice"}

    ]


推而广之,完整的JSON似乎应是这样:


    [

        {

            "name": "Mike",

            "sex": "male",

            "phones": {

                "home": "66308273",

                "cellPhone": "13907567320"

            }

        },


        {

            "name": "Alice",

            "sex": "female",

            "phones": {

                "home": "68557026",

                "cellPhone": "13693528325"

            }

        }

    ]

    

这就对了吗?array的原型是:


    [value, value, value, ...]


而object是value的一种。object的原型是:


    {string: value}

    

即,array必须符合object的要求,而object要求必须具有键值对。也就是,先有键,再有用array来表示的值系列。


因此,正确的应是:


    {

        "persons": [

            {

                "name": "Mike",

                "sex": "male",

                "phones": {

                    "home": "66308273",

                    "cellPhone": "13907567320"

                }

            },

            

            {

                "name": "Alice",

                "sex": "female",

                "phones": {

                    "home": "66308273",

                    "cellPhone": "13907567320"

                }

            }

        ]

    }

    

一句话,数组的前面应该有键,且最外层应以"{}"包围。

    

现在,看着这个JSON结构,最外层应以"{}"包围,说明代码应为其定义一个新类或新的结构,且拥有一个*persons*的变量。


    struct PersonArray: Decodable {

        var persons: [Person]

    }

    

而解析的则可改成:


    do {

        let data = try Data(contentsOf: url!)

        let decoder = JSONDecoder()

        let personArray = try decoder.decode(PersonArray.self, from: data)

        

        for person in personArray.persons {

            print("\(person.name)")

        }

    } catch {

        print("error: \(error.localizedDescription)")

    }


实际上,上面的*PersonArray*这个结构名取得不好,导致*personArray.persons*看得很别扭,如同*shoppingCar.shoppingItems*,最好将其重命名为一个表示特定范围的更加具体的集合名称,如Americans.


这就变成:


    struct Americans: Decodable {

        var persons: [Person]

    }

    

    do {

        ...

        let americanPersons = try decoder.decode(Americans.self, from: data)

        

        for person in americanPersons {

            print("\(person.name)")

        }

    }

    ...

    

## 结语

    

有了这个例子,就很容易将其复制、修改、扩充至其他领域了。


## 练习题


使用JSON表示一个包含乾、兑、离、震、巽、坎、艮、坤的八卦,并将其进行解析。


## 参考资料


1. 介绍JSON, json.org, http://www.json.org/json-zh.html

2. iOS Apprentice, Sixth Edition, Fahim Farook & Matthijs Hollemans, Chapter 34, Sect 3: Parse JSON


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值