十、移动平台上的 JSON

如今,移动应用风靡全球——平板电脑和智能移动等设备的销量在世界许多地方超过了个人电脑。 在 iOS 和 Android 等平台的支持下,这些设备包括用于创建和解析 JSON 的 api,作为平台的一部分,这让作为应用开发人员的生活更容易一些。 在本章中有以下几种方法:

  • Android 解析 JSON
  • 在 Android 上生成 JSON
  • 在 Objective-C 中解析 JSON
  • 用 Objective-C 在 iOS 上生成 JSON
  • 在 iOS 上使用 Swift 解析 JSON
  • 在 iOS 上使用 Swift 生成 JSON
  • 使用 Qt 解析 JSON
  • 使用 Qt 生成 JSON

简介

正如我们在前几章中所讨论的,JSON 是与 web 服务和客户端(无论客户端是 web 应用还是传统应用)通信的绝佳媒介。 对于移动应用来说尤其如此,许多移动应用运行在低带宽的广域网上,与 XML 相比,JSON 的简便性使得总体数据有效负载更小,从而确保远程查询的响应时间更快。

如今领先的移动平台是 Android 和 iOS。 Android 运行 Linux 的变体,支持 Java 软件开发,并在org.json命名空间中包含一个 JSON 处理器。 iOS,大致来源于 Mach 和 BSD,支持使用 Objective-C, Swift, C 和 c++进行软件开发,尽管对于大多数应用开发,你使用 Objective-C 或 Swift,每个都包含一个绑定到NSJSONSerialization类,实现 JSON 解析和 JSON 序列化。

移动开发人员的另一个选择是使用跨平台工具包(如 Qt)进行应用开发。 Qt 运行在多种平台上,包括 Android、iOS 和黑莓。 Qt 定义了QJsonDocumentQJsonObject类,您可以使用它们在映射和 JSON 之间进行转换。 Qt 是一个已经存在多年的开源框架,它不仅可以在移动平台上运行,还可以在 Mac OS X、Windows 和 Linux 以及许多其他平台上运行。

我们将在接下来的章节中讨论的 JSON 与我们在过去的章节中使用的类似,是一个像这样的文档:

{
  'call': 'kf6gpe-7',
  'lat': 37.40150,
  'lng': -122.03683
  'result': 'ok'
}

在接下来的讨论中,我假定您已经正确地为目标平台设置了软件开发环境。 描述为 Android、iOS 和 Qt 设置软件环境的过程会占用比本书更多的篇幅。 如果你有兴趣为特定的移动平台开发软件,你可能需要咨询 Android 或 iOS 的开发者资源:

Android JSON 解析

AndroidJSONObject提供了类,它可以让你代表 JSON 文档通过一个接口的名称-值对的概念上类似于一张地图,并通过 getter 和 setter 方法包括序列化和反序列化,访问 JSON 对象的命名字段。

How to do it…

首先用要解析的 JSON 初始化JSONObject,然后使用它的各种get方法来获取 JSON 字段的值:

Import org.json.JSONObject;

String json = "…";
JSONObject data = new JSONObject(data);

String call = data.getString("call");
double lat = data.getDouble("lat");
double lng = data.getDouble("lng");

How it works…

JSONObject构造函数接受 JSON 来解析,并提供访问方法来访问 JSON 的字段。 这里,我们使用getStringgetDouble访问器分别访问 JSON 的calllatlng字段。

JSONObject类定义了以下访问器:

  • get方法,它返回一个子类java.lang.Object,其中包含指定槽中的值。
  • getBoolean方法,如果槽中包含Boolean,返回Boolean
  • getDouble方法,如果槽中包含double,返回double
  • getInt方法,如果槽中包含int,返回int
  • getJSONArray方法,其中返回JSONArray的实例,如果槽包含数组,则返回处理数组的 JSON 解析类JSONArray
  • getJSONObject方法,如果槽包含另一个 map,返回一个JSONObject实例。
  • getLong方法,如果槽中包含long,返回long
  • getString方法,如果槽中包含String,则返回和String

该类还定义了hasisNull。 它们接受槽名,如果字段名中有值,返回true,如果没有字段名或值为null,则返回true

JSONArray类似于JSONObject,除了它使用数组而不是映射。 它具有相同的 getter 方法,这些方法接受集合中的整数索引,返回对象、布尔值、字符串、数字等等。

There's more…

JSONObject类还定义了keys方法,该方法返回 JSON 中键的Iterator<String>。 您还可以通过调用names获取 JSON 中名称中的JSONArray,或者通过调用length获取 JSON 中键值对的个数。

参见

更多关于JSONObject的信息,请参见 Android 文档http://developer.android.com/reference/org/json/JSONObject.html。 更多关于JSONArray的信息,请参见http://developer.android.com/reference/org/json/JSONArray.html

在 Android 上生成 JSON

JSONObject也支持 setter 方法来初始化 JSON 映射中的数据。 使用这些方法,您可以将数据分配给 JSON 对象,然后通过调用其toString方法获得 JSON 表示。

How to do it…

下面是一个简单的例子:

import org.JSON.JSONObject;

JSONObject data = new JSONObject();
data.put("call", "kf6gpe-7");
data.put("lat", 37.40150);
data.put("lng", -122.03683);
String json = data.toString();

How it works…

多态 put 方法可以接受整数、长整数、对象、布尔值或双精度值,并将指定的值分配给指定的槽。

JSONObject类定义了toString方法,该方法采用可选的空格数量来缩进嵌套结构,用于打印精美的 JSON。 如果不传递这个缩进,或者传递 0,实现将以尽可能紧凑的方式编码 JSON。

There's more…

还有putOpt方法,它接受Object的任何子类,如果 name 和 value 都是非 null,则将值赋给 name。

您可以通过传递JSONArray为槽分配值数组,或通过传递另一个JSONObject作为要设置的值来嵌套映射。 JSONArray定义了一个类似的 put 方法,它将数组中的整数索引作为第一个参数,而不是槽名。 例如,使用前一个示例中的数据对象,我可以使用以下代码添加一个台站(可能来自无线电电池)的测量电压数组:

import org.JSON.JSONObject;

JSONArray voltages = new JSONArray();
voltages.put(3.1);
voltages.put(3.2);
voltages.put(2.8);
voltages.put(2.6);
data.put("voltages", voltages);

您也可以直接放置java.util.Collectionjava.util.Map实例,而不是传递JSONArrayJSONObject实例。 前面的代码也可以写成:

import org.JSON.JSONObject;
import org.JSON.JSONArray;
import java.util.Collection;

Collection<double> voltages = new Collection<double>();
voltages.put(3.1);
voltages.put(3.2);
voltages.put(2.8);
voltages.put(2.6);
data.put("voltages", voltages);

在构造更复杂的 JSON 对象时,这让事情变得简单一些,因为您不需要将每个 Java 集合或映射包装到相应的 JSON 对象中。

参见

更多关于JSONObject的信息,请参见 Android 文档http://developer.android.com/reference/org/json/JSONObject.html。 更多关于JSONArray的信息,请参见http://developer.android.com/reference/org/json/JSONArray.html

在 iOS 上解析 JSON

Objective-C 的类库定义了NSJSONSerialization类,它可以序列化为或者从 JSON。 它将 JSON 转换为值的NSDictionary对象,带有键、JSON 中的槽名和它们的 JSON 值。 它在 iOS 5.0 及更高版本中可用。

How to do it…

下面是一个简单的例子:

NSError* error;
NSDictionary* data = [ NSJSONSerialization
  JSONObjectWithData: json
  options: kNilOptions
  error: &error ];

NSString* call = [ data ObjectForKey: @"call" ];

How it works…

NSJSONSerialization类有一个方法JSONObjectWithData:options:error,它接受一个NSString、解析选项和一个记录错误的地方,并执行 JSON 解析。 它可以接受顶级是数组或字典的 JSON,分别返回NSArrayNSDictionary结果。 所有值必须分别为NSStringNSNumberNSArrayNSDictionaryNSNull的实例。 如果顶级对象是一个数组,该方法返回NSArray; 否则,返回NSDictionary

There's more…

默认情况下,此方法返回的数据是不可变的。 如果你想要可变的数据结构,相反,你可以传递选项NSJSONReadingMutableContainers。 要解析不是数组或字典的顶级字段,请传递选项NSJSONReadingAllowFragments

参见

Apple 的文档在https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSJSONSerialization_Class/index.html

在 iOS 上生成 JSON

您可以也可以使用NSJSONSerializer类序列化NSDictionaryNSArray; 只需使用dataWithJSONObject方法。

How to do it…

下面是一个简单的例子,假设数据是NSDictionary,你想要转换成 JSON:

NSError *error;
NSData* jsonData = [NSJSONSerialization
dataWithJSONObject: data
options: NSJSONWritingPrettyPrinted
error: &error];

How it works…

dataWithJSONObject:options:error方法可以使用NSArrayNSDictionary,并返回一个带有所传递集合的编码 JSON 的NSDatablob。 如果您通过kNilOptions,JSON 将以紧凑的方式编码; 对于漂亮打印的 JSON,可以传递选项NSJSONWritingPrettyPrinted

参见

苹果的文档为NSJSONSerialization类在https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSJSONSerialization_Class/index.html

在 iOS 上使用 Swift 解析 JSON

同样的NSJSONSerialization类在苹果用于 iOS 开发的新语言 Swift 中可用。

How to do it…

下面是一个如何在 Swift 中调用NSJSONSerializationJSONObjectWithData方法的示例:

import Foundation
var error: NSError?
Let json: NSData = /* the JSON to parse */
let data = NSJSONSerialization.JSONObjectWithData(json, 
  options: nil, 
  error: &error);

How it works…

Swift 中的方法调用看起来像函数调用,参数以(可选的命名)逗号分隔的参数传递,类似于 c++或 Java 中的调用方式。 JSONObjectWithData的参数与 Objective-C 版本中的方法参数相同。

在 iOS 上使用 Swift 生成 JSON

当然,您也可以从 Swift 调用NSJSONSerialization.dataWithJSONObject方法,该方法返回一个NSData对象,然后您可以将其转换为字符串。

How to do it…

下面是一个简单的例子:

var error: NSError?
var data: NSJSONSerialization.dataWithJSONObject(
  dictionary, 
  options: NSJSONWritingOptions(0),
  error: &error);
var json: NSString(data: data, encoding: NSUTF8StringEncoding); 

How it works…

方法dataWithJSONObject操作,就像 Objective-C 方法一样。 一旦我们接收到包含 json 编码的字典版本的NSData,我们使用NSString构造函数将其转换为NSString

使用 Qt 解析 JSON

JSON 解析的 Qt 实现实际上与 Android 版本的界面非常相似。 Qt 定义了QJsonObjectQJsonArray类,它们可以分别包含 JSON 映射和 JSON 数组。 解析本身是由QJsonDocument类完成的,该类有一个静态的fromJson方法,接受 JSON 并执行必要的解析。

How to do it…

下面是一个简单的例子:

QString json = "{ 'call': 'kf6gpe-7', 'lat': 37.40150, 'lng': -122.03683, 'result': 'ok'}";
QJsonDocument document = QJsonDocument.fromJson(json);
QJsonObject data = document.object;
QString call = data["call"].toString();

How it works…

解析分为两步:首先,代码使用QJsonDocument解析 JSON,然后使用生成的QJsonObject访问数据。

QJsonObject类作为QJsonValue对象的映射,每个对象都可以使用以下方法转换为它们的基本类型:

  • toArray:此方法转换为QJsonArray
  • toBool:这个方法转换为布尔值
  • toDouble:这个方法转换为 double
  • toInt:该方法转换为整数
  • toObject:该方法转换为另一个QJsonObject,让您嵌套QJsonObject的映射
  • toString:此方法转换为QString

There's more…

您还可以使用 Qt 的foreach宏或beginconstBeginend迭代方法来遍历QJsonObject中的键。 还有一个包含方法,它接受一个槽的名称,如果映射包含您正在寻找的槽,则返回 true。

参见

关于 JSON 解析,请参阅 Qt 文档http://doc.qt.io/qt-5/json.html

使用 Qt 生成 JSON

QJsonDocument类还有toJson方法,它将引用的对象转换为 JSON。

How to do it…

下面的是一个从 JSON 转换回 JSON 的例子,在此过程中很好地打印了 JSON:

QString json = "{ 'call': 'kf6gpe-7', 'lat': 37.40150, 'lng': -122.03683, 'result': 'ok'}";
QJsonDocument document = QJsonDocument.fromJson(json);
QJsonObject data = document.object;
QByteArrayprettyPrintedJson = 
document.toJson(QJsonDocumented::Indented);    

How it works…

QJsonDocument类有一个方法toJson,它将它所引用的文档或数组转换为 JSON。 您可以通过传递QJsonDocument::Indented请求 JSON 的漂亮打印版本,或者通过传递QJsonDcoument::Compact请求 JSON 的紧凑版本。

参见

关于QJsonDocument的更多信息,请参阅http://doc.qt.io/qt-5/qjsondocument.html的 Qt 文档。