负载指的是在服务的请求响应事务中交换的数据。 比如, 在 POST 请求中, 负载指的就是请求体。 负载并不包含其他数据, 如请求头或是请求的 HTTP 方法, 例如 POST。 如果应用要向 Web Service 发送消息或是接收来自于 Web Service 的信息, 那么你需要清楚地理解请求与响应的负载格式
4.2.1 负载数据格式简介
进入与发出的负载数据存在很多形式与大小。 比如, 有些开发者会使用原生的字符串或是以分隔符分开的数据与 Web Service 进行通信。这么做虽然简单, 不过技术上却不具备可扩展性, 难以处理复杂的数据结构, 可能会导致很多问题。 本节将会介绍用于发送和接收结构化数据的 3 种标准方式, 分别是可扩展标记语言(XML)、JavaScript 对象符号(JSON)以及超文本标记语言(HTML)。
1. XML
XML 是一种标记语言, 用于编码和组织数据。 XML 规范(标准通用标记语言 SGML 的扩展) 开始于 1996 年, 由万维网联盟(W3C)制订。 第 5 次修订于 2008 年 11 月发布。 最初, XML 的关注点在于文档, 不过现在已经被广泛用作 Web Service 中传递结构化数据的格式。 XML 已经被扩展为很多标记语言和协议, 比如用于组织声音参数控制的 VoiceXML、用于交换财务数据的 Open Financial Exchange(OFX) 以及用于发布媒体内容的 Really Simple Syndication(RSS)等。 上一节曾提到过, SOAP 协议也通过 XML 来交换结构化数据。
XML 文档包含标记与内容、标记由标签、属性与元素构成。 有 3 种类型的标签: 起始标签(<person>)、结束标签(</person>)以及空元素标签(<noContact />)。 空元素标签也叫做自关闭标签。 属性指的是起始标签或空元素标签中的键值对, 它们提供了关于元素的附加信息.
元素指的是构成 XML 文档的组件. 元素是标签、属性与内容的集合. 元素包含起始标签与结束标签或是空元素标签. 起始标签与结束标签之间的数据就是内容. 内容可以包含标记与其他元素, 这样就可以在数据结构中构建父子关系了. 下述代码片段展示了一个 XML 元素示例:
<person>
<firstName>Nathan</firstName>
<lastName>Jones</lastName>
<emailAddress primary = 'true'>email@domain.com</emailAddress>
<noContace medium = 'email' />
</person>上述代码描述了一个名为 person 的元素, 该元素包含几个子元素: firstName、lastName、emailAddress 与 noContact. emailAddress 元素包含一个属性, 用于表明该元素的内容是这个人的主 E-Mail 地址. noContact 元素 (表示这个人不想被联系) 也包含了一个属性, 用于表明不应该通过哪种方式联系这个人
2. JSON
JSON 是一种用于交换结构化信息的轻量级数据格式.
JSON 拥有小巧的格式规则定义集合, 在创建负载时需要严格遵守. 下面是 JSON 支持的数据类型以及与之关联的格式规则:
- 数字: 无双引号
- 布尔: 取值为 true 或 false, 无双引号
- 字符串: 双引号括起
- 数组: 方括号包围的以逗号分隔的列表
- 对象: 花括号的键值对集合. Objective-C 中的对象是通过 NSDictionary 表示的
- null: 无双引号格式良好的 JSON 文档的根类型要么是数组, 要么是对象. 如下代码片段使用 JSON 表示之前用 XML 表示的 person 示例:
{
"person": {
"firstName": "Nathan",
"lastName": "Jones",
"email": {
"emailAddress": "email@domain.com",
"primary": true
},
"noContact": "email"
}
}3. HTML
HTML 是一种标记语言, 用于组织网页上的数据, 这样浏览器就可以解析页面了.
HTML 文档结构类似于 XML 文档, 它们都起源于 SGML. 然而, HTML 新的草案(HTML5) 并不像之前的版本那样基于 SGML. HTML 文档包含 doctype 定义(DTD)、元素、属性、数据类型与字符实体引用. HTML 与 XML 文档结构的主要差别在于 HTML 文档拥有预先定义好的标签与属性名的集合.
doctype 定义位于 HTML 文档的第一行, 它告诉浏览器当前页面使用的是 HTML 规范的哪个版本. 元素的属性是位于起始标签中的键值对. 不过 HTML5 还支持自定义属性. 这些属性以 data- 作为前缀, 并且不应该包含大写字母. 自定义属性旨在存储不适合现有属性存储的特定于应用的数据.
4.2.2 解析响应负载
1. XML
SAX 解析器是事件驱动的, 它会顺序解析 XML 文档中的元素, 一次处理一个元素.
DOM 解析器则会将整个 XML 文档以可遍历的结点树的形式读取到内存中.iOS 自带了两种原生 XML 解析器, 分别是 NSXMLParser 与 libxml. NSXMLParser 是个 Objective-C SAX 解析器, 在遇到元素、属性、CData 块、注释与文档起始和结束事件时会调用各种委托方法. libxml 是个开源、基于 C 语言的 API , 支持 SAX 与 DOM 解析. libxml SAX 解析类似于 NSXMLParser, 因为在遇到某些事件时会进行大量回调. libxml DOM 会将整个 XML 文档读取为结点树, 可以通过 XML Path Language(XPath) 遍历与查询. 这里的示例使用了 NSXMLParser, 不过在稍后解析 HTML 时会使用 libxml.
在决定选择解析器时, 预期的 XML 文档大小是个重要的考虑因素:
NSXMLParser 委托:
- parserDidStartDocument: 解析器开始解析 XML 种子时调用
- parserDidEndDocument: 解析器到达文档末尾时调用
- parser:didStartElement:namespaceURI:qualifiedName:attributes: 开始处理新元素时调用
- parser:foundCharacters: 当从元素中读取内容时调用
- parser:didEndElement:namespaceURI:qualifiedName: 当元素关闭时调用
2. HTML
HTML 文档的结构类似于 XML. 不过, XML 文档的结构要求发送端与接收端遵循某种服务契约.
考虑到 HTML 文档很容易变化这个问题, 你不应该在应用中解析 HTML, 变更会极大地影响应用的正常使用
3. JSON
NSJSONSerialization 的 options 参数
- NSJSONReadingAllowFragments: 告诉解析器处理既不是 NSArray 也不是 NSDictionary 的顶层对象. 这个选项可以处理诸如 {"user": null} 这样的简单 JSON 结构的转换
- NSJSONReadingMutableContainers: 告诉解析器生成 NSMutableArray 与 NSMutableDictionary 对象. 可变对象意味着可以通过方法修改它们, 比如 NSMutableArray 的 addObject: 与 NSMutableDictionary 的 setObject:forKey:. 这可以用于如下场景: 有主要和次要的结果集, 你要在进一步处理前将次要结果中的值添加到主要结果中.
-NSJSONReadingMutableLeaves: 告诉解析器生成 NSMutableString 对象. 如果要在进一步处理前操纵被解析的响应中的某个特定值, 那么可以使用该选项.
4.2.3 生成请求负载
1. JSON
NSJSONSerialization 还提供了 isValidJSONObject: 来验证尝试转换的 Foundation 对象是否可以转换成 JSON. 对于能够转换为 JSON 的对象来说, 必须满足如下规则:
- 顶层对象是 NSArray 或 NSDictionary
- 所有的对象必须是 NSString、NSNumber、NSArray、NSDictionary 或 NSNull
- 所有的 NSDictionary 键必须是 NSStrings
- NSNumbers 不能为 NaN 或无穷大