在 本系列最近發表的一篇文章中,您已經瞭解了如何將 JavaScript 對象轉變成 JSON 格式。這種格式很容易用於發送(和接收)與對象甚至對象數組對應的資料。在本系列 的最後一篇文章中,您將會學習如何處理以 JSON
格式發送到伺服器的資料以及如何使用相同格式對指令碼進行回複。
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 資料中會有空格和各種字元,網頁瀏覽器往往要嘗試對其繼續編譯。要確保這些字元不會在伺服器上(或者在將資料發送給伺服器的過程中)引起混亂,需要在 JavaScript escape() 函數中做如下添加:
var url = "organizePeople.php?people=" + escape(people.toJSONString());
request.open("GET", url, true);
request.onreadystatechange = updatePage;
request.send(null);
該函數可以處理空格、斜線和其他任何可能影響瀏覽器的內容,並將它們轉換成 Web 可用字元(比如,空格會被轉換成 %20,瀏覽器並不會將其視為空白格處理,而是不做更改,將其直接傳遞到伺服器)。之後,伺服器會(通常自動)再把它們轉換回它們傳輸後的本來 “面目”。
這種做法的缺點有兩個:
1、在使用 GET 請求發送大塊資料時,對 URL 字串有長度限制。雖然這個限制很寬泛,但對象的 JSON 字串表示的長度可能超出您的想象,尤其是在使用極其複雜的對象時更是如此。
2、在跨網路以純文字發送所有資料的時候,發送資料面臨的不安全性超出了您的處理能力。
簡言之,以上是 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 基本上就需要兩個步驟。
1、針對編寫伺服器端程式所用的語言,找到相應的 JSON 解析器/工具箱/協助器 API。
2、使用 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...
}
結束語
至此,您應該從技術角度對如何在伺服器端處理 JSON 有了基本的把握。本篇文章和本系列的 第 10 部分 不僅提供了技術層面的協助,而且還向您展示了 JSON 是一種多麼靈活、強大的資料格式。即使您不會在每個應用程式中都使用 JSON,但優秀的 Ajax 和 JavaScript 程式員的工具箱中總少不了 JSON 以備不時之需。
我當然很希望能夠分享您的 JSON 使用經驗以及您對何種語言更善於在伺服器端處理 JSON 資料的高見。您可以訪問 Java 和 XML 新聞群組(有關連結,請參閱 參考資料 部分)跟我聯絡。享受 JSON 和文本資料格式的樂趣吧。