在構建任何網絡感知軟件時,選擇正確的數據交換格式都是一項重要的設計決策。在設計移動和嵌入式應用程序時尤其如此,此時,輕量 和有效 等特性是需要考慮的重要特征。此類特征非常重要,因為它們可轉化為較低的計算要求和功耗使用、可能更好的性能以及較低的運營成本。
在移動應用程序中,開發者通常依賴於自己創建的數據交換格式或可擴展標記語言 (Extensible Markup Language, XML)。前者的優勢是可以針對特定情況進行調整,從而最大限度地提高性能並且/或者最大限度地利用計算資源。當在 HTTP 上使用後者時,其優勢在於它是事實上的數據交換標准。另外,在 XML 中使用的基於文本的/可讀的表示形式使其比較易於調試。
然而,這兩種方法也有缺陷,第一種方法在本質上是專用的、非標准的並可能是不可互操作的,而第二種方法可能被認為對數據表示而言過於正規和繁瑣,對於移動和嵌入式應用程序而言尤其如此。
一種可以考慮的備選方法是 Javascript Object Notation (JSON),這是一種輕量級數據交換格式。在本文中,我將介紹如何在 Java Platform, Micro Edition (Java ME) 中使用 JSON 進行數據交換。
JSON 被定義為 JavaScript (ECMAScript) 腳本語言的一部分。由於 JSON 是 Javascript 本身所固有的,因此對基於浏覽器的應用程序而言,JSON 是一種理想的方法。但 JSON 並不僅限於 JavaScript,一般而言,它所具有的“輕量”特征使其對移動和嵌入式應用程序而言非常有吸引力。
JSON 使用非常簡單的語法,該語法基於兩個主要的數據結構:
名稱/值對的集合,以及
值的順序列表
JSON 是基於文本的,這使它具有很好的可讀性,並且非常易於調試。JSON 支持所有基本數據類型的表示法,並且提供了將這些數據類型來回解析為 Java 本機類型的方法。下表總結了 JSON 語法元素:
表 1 - JSON 語法元素
類型 起始符 終止符 包含或是... 示例 對象 { } 成員 {...} 成員 對、其他成員 對 名稱(字符串) 值 由“:”分隔的名稱和值 "FirstName":"Enrique" 名稱 屬性名 : 一個字符串 "username" 值 : 實際值 字符串、數字、對象、數組、null、布爾值(true、false) "C. Enrique Ortiz", true, 01234, [...], {...} 數組 [ ] 元素 ["mobile", "web", "aPPS"] 元素 值、其他元素
下面的 JSON 文本片段顯示了 JSON-Time 的請求和響應,這是一個有用的 Web 服務,它返回指定時區的當前時間。以下示例顯示美國中部時間的請求和響應:
清單 1 - JSON-Time HTTP 請求和 JSON 文本響應
請求:
http://json-time.aPPSpot.com/time.JSon?tz=US/Central
響應:
{
"tz": "US/Central",
"hour": 15,
"datetime": "Tue, 05 Aug 2008 15:02:27 -0500",
"second": 27,
"error": false,
"minute": 2
}
響應是一個簡單的 JSON 文本結構,即一個包含正確轉義的值、字符串、數字和布爾值的未命名對象。
JSON 支持的數據類型
JSON 支持所有基本數字和字符串數據類型。數字是整數數字、小數或指數,但是請記住,CLDC 1.0 中不支持浮點/雙精度數據類型。字符串是任何 Unicode 字符。
必須使用反斜槓字符正確對下列字符進行轉義:
引號 (")
反斜槓 ()
正斜槓 (/)
退格 (b)
換頁 (f)
換行 (n)
回車 (r)
制表 (t)
十六進制數字 (u4-hex-digits)
有關 JSON 語法的更多信息,請訪問 JSON.org。
Java ME 中的 JSON
作為移動和嵌入式應用程序開發者項目的一部分,Sun Microsystems 提供了 JSON for ME Java API 的開源 (Java.Net) 項目版本。此源代碼適當配置了檢測機制,以便針對 CLDC 1.0 或 1.1 進行條件編譯(即,浮點支持)。還可以在 JSON.org 獲得 JSON ME 源代碼,但不包含條件指令。
如您所料,JSON ME 是完整 JSON Java API 的子集。下表總結了 JSON Java API:
表 2 - JSON ME Java API
包名稱 類 org.JSon.me JSONArray 是值的有序序列
JSONException 是檢測到的錯誤
JSONObject 是名稱/值對的有序集合
JSONString 是 JSON 序列化的接口
JSONStringer 是用於生成 JSON 語法的 JSONWriter 的子類
JSONTokener 是由 JSONObject 和 JSONArray 使用的解析工具類
JSONWriter 是用於生成 JSON 語法的便利類
StringWriter 是 StringWriter 的基於 StringBuffer 的實現。這是一個 Sun Microsystems 幫助類,而不是標准 JSON Java API 的一部分
org.JSon.me.util XML 是一個用於從 XML 生成 JSON 的幫助類XMLTokener 在 XML 語法解析支持方面擴展了 JSONTokener
JSON 和 JSON ME 的內部機制有所不同,表現在下列方面:所使用的一些數據類型(例如,JSON ME 中使用的 Vector 和Hashtable),以及用於進行條件編譯以支持浮點的的編譯器指令 (CLDC 1.1)。JSON ME JAR 文件的大小約為 25KB。
使用 JSON
JSON Java API 提供了一些便利類(例如,JSONWriter 和 JSONStringer)以生成 JSON 文本。但是,我個人傾向於直接將核心 JSONObject 與 JSONArray 一起使用,這可以提供所需的解析功能。下面,我們將討論幾個簡單的示例,在這些示例中,我們使用前面提到的那些核心類將一個簡單的 DataTypes 示例類序列化到 JSON 以及從 JSON 反序列化這些示例類。以下代碼片段演示了我們將用作示例的 JSON 文本:
清單 2 - JSON 文本示例
{
"datatypes": {
"aString":"C. Enrique Ortiz",
"anArray":["tech","mobile","web", "aPPS"],
"aInteger": 15569,
"aLong": 1234567890,
"aBoolean": true,
}
}
看起來足夠簡單,是嗎?使用 JSON 可以在對象內嵌入對象,並且可以像在 XML 中那樣定義任何數據結構。
核心 JSON JSONObject 類
JSONObject 是可使用的核心 JSON Java 類。該類提供了許多幫助方法,例如可完成以下任務的方法:
在鍵下面 accumulate 值
向鍵下的數組 append 值
從 double 類型值生成 String 類型值
get 一個 Java Object、一個 boolean 類型值、一個 double 類型值、一個 int 類型值、一個 JSONArray 類型值、一個 JSONObject 類型值、一個 long 類型值以及 put 相同數據類型值的逆方法
get JSONObject 內的字段名
get 與特定鍵關聯的值
確定 JSONObject 內是否存在某個特定鍵
確定與該鍵關聯的值是否為 null 或者是否沒有值
獲取 JSONObject 的鍵的 Enumeration
獲取 JSONObject 中存儲的鍵的數量
獲取包含此 JSONObject 的元素名的 JSONArray
從數字生成 String 類型值
在雙引號中生成一個 String,並且使反斜槓序列都處於正確的位置
刪除一個名稱和它的值(如果存在)
獲取 JSONObject 的鍵的 Enumeration
生成一個包含此 JSONObject 的成員值的 JSONArray
生成此 JSONObject 的 JSON 文本
示例 DataTypes 類
以下代碼片段定義了一個 DataTypes 示例 Java 類,該類包含 JSON ME 所支持的數據類型。我們將使用該類來說明如何序列化到 JSON 以及從 JSON 進行反序列化。同樣請記住,CLDC 1.0 中不支持浮點/雙精度類型。
清單 3 - 數據類型示例類
/**
* A data types class to use as example
*/
class DataTypes {
public String aString; // a string
public String[] anArray; // an array
public int anInteger; // an integer
//public double aDouble; // double not supported on CLDC 1.0
public long aLong; // a long
public boolean aBoolean; // a boolean
/**
* An example multi-data type Class
*/
public DataTypes(
String aString,
String[] anArray,
int anInteger,
//double aDouble, // Not supported on CLDC 1.0
long aLong,
boolean aBoolean) {
this.aString = aString;
this.anArray = anArray;
this.anInteger = anInteger;
//this.aDouble = aDouble; // Not supported on CLDC 1.0
this.aLong = aLong;
this.aBoolean = aBoolean;
}
:
:
}
支持的數據類型有:String、int、long、boolean 以及前面提到的這些基本數據類型的數組。
序列化到 JSON:生成 JSON 文本
現在,讓我們創建 DataTypes 類實例的 JSON 文本表示。請注意,盡管 JSON Java API 提供了一些幫助類(JSONWriter 和 JSONStringer),以使您可以在 Java 中使用類似於 JSON 的語法流,但下面的示例直接使用 JSONOjbect 和 JSONArray 這些簡單而高效的 API。另外,可以輕松地將此方法中使用的代碼流映射到用於生成 XML 的代碼流。下面的代碼片段說明了 toJSON() 序列化幫助方法:
清單 4 - 生成 JSON 文本
/**
* Serializes this DataTypes instance
*
* @return the serialized DataTypes as JSON text
*/
public String toJSON() {
// Define an external an a nexted JSONObjects
JSONObject outer = new JSONObject();
JSONObject inner = new JSONObject();
// Now generate the JSON output
try {
outer.put("datatypes", inner); // the outer object name
inner.put("aString", aString); // a name/value pair
JSONArray ja = new JSONArray();
for (int i=0; i<anArray.length; i++) {
ja.put(anArray[i]);
}
inner.put("anArray", ja); a name/value pair
inner.put("anInteger", anInteger); a name/value pair
//inner.put("aDouble", aDouble); // Not supported on CLDC 1.0
inner.put("aLong", aLong); a name/value pair
inner.put("aBoolean", aBoolean); a name/value pair
} catch (JSONException ex) {
// ...process exception
}
return outer.toString(); // return the JSON text
}
注意:使用硬編碼文本字面值不是一種好的做法,但是,為了進行說明,我已經在該示例代碼片段中使用了它們。
反序列化 JSON:從 JSON 文本初始化類
以下代碼片段說明了幫助方法 fromJSON(),該方法反序列化一個輸入 JSON 文本字符串,並且初始化 DataType 類的實例:
清單 5 - 從輸入 JSON 文本字符串初始化對象
/**
* Initializes this instance of UserInfo and de-serializes the
* input JSON string
*
* @param ji is the input JSON string
*/
public void fromJSON(String ji) {
// First, clear the object.
aString = null;
anArray = null;
anInteger = 0;
//aDouble = 0.0; // Double not supported on CLDC 1.0
aLong = 0;
aBoolean = false;
// Now initialize from JSON text.
try {
JSONObject outer = new JSONObject(ji); // the outer objet
if (outer != null) {
// Get the inner object and parse out the data
JSONObject inner = outer.getJSONObject("datatypes");
if (inner != null) {
// Parse the name/value pairs
aString = inner.getString("aString");
JSONArray ja = inner.getJSONArray("anArray");
if (ja != null) {
anArray = new String[ja.length()];
for (int i=0; i<ja.length(); i++) {
anArray[i] = (String) ja.get(i);
}
}
anInteger = inner.getInt("anInteger");
//aDouble = inner.getDouble("aDouble");
aLong = inner.getLong("aLong");
aBoolean = inner.getBoolean("aBoolean");
}
}
} catch (Exception e) {
// ...process exception
}
}
使用序列化方法
以下代碼片段說明如何使用前面的 toJSON() 和 fromJSON() JSON 序列化方法:
清單 6 - 使用 JSON 序列化方法
// Create an initialize instance of DataTypes
DataTypes dt = new DataTypes(
"C. Enrique Ortiz", // a String
new String[] {"tech","mobile","web", "aPPS"}, // an Array
15569, // an int
//0.0, // a double, not supported on CLDC 1.0
1234567890, // a long
true); // a boolean
// Covert object to JSON
String j = dt.toJSON();
System.out.println("*** toJSON: " + j);
// Initialize object from JSON
System.out.println("*** fromJSON:");
dt.fromJSON(j);
// Dump to console to see if it worked
dt.dump();
以下是前面的代碼中使用的用於進行調試的 dump() 幫助方法:
清單 7 - 用於進行調試的 dump() 幫助方法
/**
* Dump DataTypes for debugging
*/
public void dump() {
System.out.println(" aString: " + aString);
if (anArray != null) {
for (int i =0; i<anArray.length; i++) {
System.out.println(" tag [" + i + "]: " + anArray[i]);
}
}
System.out.println(" anInteger: " + anInteger);
//System.out.println(" aDouble: " + aDouble);
System.out.println(" aLong: " + aLong);
System.out.println(" aBoolean: " + aBoolean);
}
結束語
在本文中,我已經介紹了作為 XML 的備選方法的 JSON for ME。JSON 所具有的輕量特征使其對移動和嵌入式應用程序充滿吸引力。使用 JSON for ME Java API(由 Sun Microsystems 貢獻)可以輕松地解析 JSON 文本。該 API 足夠小,並且提供了您在應用程序中交換數據所需的全部功能。