JSON 的真正價值
正如在 本系列上一篇文章 中所描述的那樣,JSON 是適用於 Ajax 應用程序的一種有效格式,原因是它使 Javascript 對象和字符串值之間得以快速轉換。由於 AJax 應用程序非常適合將純文本發送給服務器端程序並對應地接收純文本,相比不能生成文本的 API,能生成文本的 API 自然更可取;而且,JSON 讓您能夠處理本地 JavaScript 對象,而無需為如何表示這些對象多費心思。
XML 也可以提供文本方面的類似益處,但用於將 Javascript 對象轉換成 XML 的幾個現有 API 沒有 JSON API 成熟;有時,您必須在創建和處理 JavaScript 對象時格外謹慎以確保所進行的處理能與所選用的 XML 會話 API 協作。但對於 JSON,情況就大不相同:它能處理幾乎所有可能的對象類型,並會返回給您一個非常好的 JSON 數據表示。
因此,JSON 的最大價值在於可以將 JavaScript 真的作為 Javascript 而非數據格式語言進行處理。您所學到的所有有關使用 JavaScript 對象的技巧都可以應用到代碼中,而無需為如何將這些對象轉變成文本而多費心思。這之後,可以進行如下所示的簡單 JSON 方法調用:
String myObjectInJSON = myObject.toJSONString();
現在就可以將結果文本發送給服務器了。
將 JSON 發給服務器
將 JSON 發給服務器並不難,但卻至關重要,而且還有一些重要的選擇要做。但是,一旦決定使用 JSON,所要做的這些選擇就會十分簡單而且數量有限,所以您需要考慮和關注的事情不多。重要的是能夠將 JSON 字符串發送給服務器,而且最好能做到盡快和盡可能簡單。
通過 GET 以名稱/值對發送 JSON
將 JSON 數據發給服務器的最簡單方法是將其轉換成文本,然後以名稱/值對的值的方式進行發送。請務必注意,JSON 格式的數據是相當長的一個對象,看起來可能會如清單 1 所示:
清單 1. JSON 格式的簡單 JavaScript 對象
var people = { "programmers": [ { "firstName": "Brett", "lastName":"McLaughlin",
"email": "brett@newInstance.com" }, { "firstName": "Jason", "lastName":"Hunter",
"email": "jason@servlets.com" }, { "firstName": "Elliotte", "lastName":"Harold",
"email": "elharo@Macfaq.com" } ], "authors": [ { "firstName": "Isaac",
"lastName": "Asimov", "genre": "scIEnce fiction" }, { "firstName": "Tad",
"lastName": "Williams", "genre": "fantasy" }, { "firstName": "Frank",
"lastName": "Peretti", "genre": "christian fiction" } ], "musicians": [
{ "firstName": "Eric", "lastName": "Clapton", "instrument": "guitar" },
{ "firstName": "Sergei", "lastName": "Rachmaninoff", "instrument": "piano" } ] }
如果要以名稱/值對將其發送到服務器端,應該如下所示:
var url = "organizePeople.PHP?people=" + people.toJSONString();
XMLHttp.open("GET", url, true);
XMLHttp.onreadystatechange = updatePage;
XMLHttp.send(null);
這看起來不錯,但卻存在一個問題:在 JSON 數據中會有空格和各種字符,Web 浏覽器往往要嘗試對其繼續編譯。要確保這些字符不會在服務器上(或者在將數據發送給服務器的過程中)引起混亂,需要在 JavaScript escape() 函數中做如下添加:
var url = "organizePeople.PHP?people=" + escape(people.toJSONString());
request.open("GET", url, true);
request.onreadystatechange = updatePage;
request.send(null);
該函數可以處理空格、斜線和其他任何可能影響浏覽器的內容,並將它們轉換成 Web 可用字符(比如,空格會被轉換成 %20,浏覽器並不會將其視為空格處理,而是不做更改,將其直接傳遞到服務器)。之後,服務器會(通常自動)再把它們轉換回它們傳輸後的本來 “面目”。
這種做法的缺點有兩個:
在使用 GET 請求發送大塊數據時,對 URL 字符串有長度限制。雖然這個限制很寬泛,但對象的 JSON 字符串表示的長度可能超出您的想象,尤其是在使用極其復雜的對象時更是如此。
在跨網絡以純文本發送所有數據的時候,發送數據面臨的不安全性超出了您的處理能力。
簡言之,以上是 GET 請求的兩個限制,而不是簡單的兩個與 JSON 數據相關的事情。在想要發送用戶名和姓之外的更多內容,比如表單中的選擇時,二者可能會需要多加注意。若要處理任何機密或極長的內容,可以使用 POST 請求。
利用 POST 請求發送 JSON 數據
當決定使用 POST 請求將 JSON 數據發送給服務器時,並不需要對代碼進行大量更改,如下所示:
var url = "organizePeople.PHP?timeStamp=" + new Date().getTime();
request.open("POST", url, true);
request.onreadystatechange = updatePage;
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
request.send(people.toJSONString());
這些代碼中的大部分,您都在 “ 掌握 Ajax,第 3 部分:AJax 中的高級請求和響應” 中見過,應該比較熟悉,第 3 部分重點介紹了如何發送 POST 請求。請求使用 POST 而非 GET 打開,而且 Content-Type 頭被設置為讓服務器預知它能得到何種數據。在這種情況下,即為 application/x-www-form-urlencoded,它讓服務器知道現在發送的是文本,正如它從常規的 Html 表單中得到的一樣。
另一個簡單提示是 URL 的末尾追加了時間。這就確保了請求不會在它第一次被發送後即緩存,而是會在此方法每次被調用後重新創建和重發;此 URL 會由於時間戳的不同而稍微有些不同。這種技巧常被用於確保到腳本的 POST 每次都會實際生成新請求且 Web 服務器不會嘗試緩存來自服務器的響應。
JSON 就只是文本
不管使用 GET 還是 POST,關鍵之處在於 JSON 就只是文本。由於不需要特殊編碼而且每個服務器端腳本都能處理文本數據,所以可以輕松利用 JSON 並將其應用到服務器。假如 JSON 是二進制格式的或是一些怪異的文本編碼,情況就不這麼簡單了;幸好 JSON 只是常規的文本數據(正如腳本能從表單提交中所接收到的數據,在 POST 段和 Content-Type 頭中亦可以看出),所以在將數據發送到服務器時無需太費心。
在服務器上解釋 JSON
一旦您編寫完客戶端 Javascript 代碼、允許用戶與 Web 表單和 Web 頁的交互、收集發送給服務器端程序以做處理所需的信息,此時,服務器就成為了應用程序(如果調用了異步使用的服務器端程序,則可能是我們認為的所謂的 “AJax 應用程序”)中的主角。在此時,您在客戶端所做的選擇(比如使用 JavaScript 對象,然後將其轉換成 JSON 字符串)必須要與服務器端的選擇相匹配,比如使用哪個 API 解碼 JSON 數據。
處理 JSON 的兩步驟
不管在服務器端使用何種語言,在服務器端處理 JSON 基本上就需要兩個步驟。
針對編寫服務器端程序所用的語言,找到相應的 JSON 解析器/工具箱/幫助器 API。
使用 JSON 解析器/工具箱/幫助器 API 取得來自客戶機的請求數據並將數據轉變成腳本能理解的東西。
以上差不多就是目前所應了解的大致內容了。接下來,我們對每個步驟進行較為詳細的介紹。
尋找 JSON 解析器
尋找 JSON 解析器或工具箱最好的資源是 JSON 站點(有關鏈接,請參閱 參考資料)。在這裡,除了可以了解此格式本身的方方面面之外,還可以通過各種鏈接找到 JSON 的各種工具和解析器,從 ASP 到 Erlang,到 Pike,再到 Ruby,應有盡有。您只需針對自己編寫腳本所用的語言下載相應的工具箱即可。為了讓服務器端腳本和程序能夠使用此工具箱,可以根據情況對其進行選擇、擴展或安裝(如果在服務器端使用的是 C#、PHP 或 Lisp,則可變性更大)。
例如,如果使用的是 PHP,可以簡單將其升級至 PHP 5.2 並用它完成操作;在 PHP 這個最新版本默認包含了 JSON 擴展。實際上,那也是在使用 PHP 時處理 JSON 的最好方法。如果使用的是 Java servlet,json.org 上的 org.json 包顯然就是個不錯的選擇。在這種情況下,可以從 JSON Web 站點下載 JSon.zip 並將其中包含的源文件添加到項目構建目錄。編譯完這些文件後,一切就就緒了。對於所支持的其他語言,同樣可以使用相同的步驟;使用何種語言取決於您對該語言的精通程度,最好使用您所熟悉的語言。
使用 JSON 解析器
一旦獲得了程序可用的資源,剩下的事就是找到合適的方法進行調用。比如,假設為 PHP 使用的是 JSON-PHP 模板:
// This is just a code fragment from a larger PHP server-side script
require_once('JSON.PHP');
$json = new Services_JSON();
// accept POST data and decode it
$value = $JSon->decode($GLOBALS['HTTP_RAW_POST_DATA']);
// Now work with value as raw PHP
通過該模板,可將獲得的所有數據(數組格式的、多行的、單值的或 JSON 數據結構中的任何內容)轉換成原生 PHP 格式,放在 $value 變量中。
如果在 servlet 中使用的是 org.JSon 包,則會使用如下代碼:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
StringBuffer jb = new StringBuffer();
String line = null;
try {
BufferedReader reader = request.getReader();
while ((line = reader.readLine()) != null)
jb.append(line);
} catch (Exception e) { //report an error }
try {
JSONObject jsonObject = new JSONObject(jb.toString());
} catch (ParseException e) {
// crash and burn
throw new IOException("Error parsing JSON request string");
}
// Work with the data using methods like...
// int someInt = JSonObject.getInt("intParamName");
// String someString = JSonObject.getString("stringParamName");
// JSONObject nestedObj = jsonObject.getJSONObject("nestedObjName");
// JSONArray arr = jsonObject.getJSONArray("arrayParamName");
// etc...
}
可以參考 org.json 包文檔(有關鏈接,請參閱 參考資料 部分)以了解詳細信息。(注意:如果想要獲得有關 org.json 或其他 JSON 工具箱的詳細信息,可以給我發電子郵件。您的來信會有助於我決定將來大概寫的內容!)
結束語
至此,您應該從技術角度對如何在服務器端處理 JSON 有了基本的把握。本篇文章和本系列的 第 10 部分 不僅提供了技術層面的幫助,而且還向您展示了 JSON 是一種多麼靈活、強大的數據格式。即使您不會在每個應用程序中都使用 JSON,但優秀的 AJax 和 JavaScript 程序員的工具箱中總少不了 JSON 以備不時之需。
我當然很希望能夠分享您的 JSON 使用經驗以及您對何種語言更善於在服務器端處理 JSON 數據的高見。您可以訪問 Java 和 XML 新聞組(有關鏈接,請參閱 參考資料 部分)跟我聯系。享受 JSON 和文本數據格式的樂趣吧。