JavaScript 和 .NET 中的 JavaScript 物件標記法 (JSON) 簡介

 

JavaScript 和 .NET 中的 JavaScript 物件標記法 (JSON) 簡介

Atif Aziz、Scott Mitchell

2007 年 2 月

適用於:
   JSON
   Ajax

總結: 本文討論 JavaScript 物件標記法 (或 JSON) ,這是開放式和以文字為基礎的資料交換格式,可提供更適合 Ajax 樣式 Web 應用程式的標準化資料交換格式。 (22 個列印頁面)

目錄

簡介
瞭解 JavaScript 中的常值標記法
比較 JSON 與 XML
使用 JavaScript 建立和剖析 JSON 訊息
在.NET Framework中使用 JSON
結論
參考資料

下載本文的原始程式碼

簡介

設計將會與遠端電腦通訊的應用程式時,必須選取資料格式和交換通訊協定。 有各種開放、標準化的選項,而且理想的選擇取決於應用程式需求和既有的功能。 例如,SOAP 型 Web 服務會將 XML 承載中的資料格式化,包裝在 SOAP 信封內。

雖然 XML 適用于許多應用程式案例,但有一些缺點,使其不適用於其他案例。 XML 通常比理想的空間之一,就是使用 Ajax 樣式的 Web 應用程式。 Ajax 是一種用來建置互動式 Web 應用程式的技術,可透過使用頻外、輕量型呼叫網頁伺服器來提供貼齊器使用者體驗,而不是完整頁面回傳。 這些非同步呼叫會使用 JavaScript 在用戶端上起始,並涉及格式化資料、將它傳送至網頁伺服器,以及剖析和使用傳回的資料。 雖然大部分的瀏覽器都可以建構、傳送和剖析 XML、JavaScript 物件標記法 (或 JSON) 提供更適合 Ajax 樣式 Web 應用程式的標準化資料交換格式。

JSON 是開放、以文字為基礎的資料交換格式, (請參閱 RFC 4627) 。 就像 XML 一樣,它是人類看得懂的平臺獨立,而且享有廣泛的實作可用性。 根據 JSON 標準格式化的資料是輕量型的,而且 JavaScript 實作可以輕易地剖析,使其成為 Ajax Web 應用程式的理想資料交換格式。 因為它主要是資料格式,JSON 不限於 Ajax Web 應用程式,而且幾乎可以在任何需要交換或儲存結構化資訊做為文字的案例中使用。

本文會檢查 JSON 標準、其與 JavaScript 的關聯性,以及其與 XML 的比較方式。 Jayrock是適用于 .NET 的開放原始碼 JSON 實作,會在 JavaScript 和 C# 中提供建立和剖析 JSON 訊息的範例。

瞭解 JavaScript 中的常值標記法

常值用於程式設計語言,以 值表示固定值,例如常數整數值為 4 或字串 「Hello, World」。每當允許運算式時,常值都可以用於大部分的語言,例如控制項語句中的條件一部分、呼叫函式時的輸入參數、變數指派等等。 例如,下列 C# 和 Visual Basic 程式碼會使用常數整數值42初始化變數x

  
    int x = 42;  // C#
Dim x As Integer = 42  ' Visual Basic
  

不同的程式設計語言允許不同類型的常值。 大部分的程式設計語言至少支援純量類型的常值,例如整數、浮點數、字串和布林值。 JavaScript 的有趣之處在于,除了純量類型之外,它也支援陣列和物件等結構化類型的常值。 這項功能允許隨選建立和初始化陣列和物件的三維語法。

JavaScript 中的陣列常值是由零個或多個運算式所組成,每個運算式都代表陣列的專案。 陣列元素會以方括弧括住, ([]) 並以逗號分隔。 下列範例會以常 方式定義陣列,其中包含七個大陸的名稱字串元素:

  
    var continents = ["Europe", "Asia", "Australia", "Antarctica", "North
 America", "South America", "Africa"];
alert(continents[0] + " is one of the " + continents.length + "
 continents.");
  

現在請比較這與如何在 JavaScript 中建立和初始化陣列,而不使用常值標記法:

  
    var continents = new Array();
continents[0] = "Europe";
continents[1] = "Asia";
continents[2] = "Australia";
continents[3] = "Antarctica";
continents[4] = "North America";
continents[5] = "South America";
continents[6] = "Africa";
  

物件常值會定義物件的成員及其值。 物件成員和值的清單會以大括弧括住 () {} ,而且每個成員都是以逗號分隔。 在每個成員內,名稱和值會以冒號分隔 () 。 下列範例會建立 物件,並以名為 AddressCityPostalCode 的三個成員初始化物件,其值為 「123 Anywhere St.」、「Springfield」 和 「99999」。

  
    var mailingAddress = { 
     "Address"    :   "123 Anywhere St.", 
     "City"       :   "Springfield", 
     "PostalCode" :   99999
};
alert("The package will be shipped to postal code " +
 mailingAddress.PostalCode);
  

到目前為止所呈現的範例說明如何在陣列和物件常值中使用字串和數值常值。 您也可以以遞迴方式使用標記法來表示整個圖表,讓陣列元素和物件成員值可以自行使用物件和陣列常值。 例如,下列程式碼片段說明具有陣列做為成員的物件, (PhoneNumbers) ,其中陣列是由物件清單所組成。

  
    var contact = {
     "Name": "John Doe",
     "PermissionToCall": true,
     "PhoneNumbers": [ 
       {
           "Location": "Home",
           "Number": "555-555-1234"
       },
       {
           "Location": "Work",
           "Number": "555-555-9999 Ext. 123"
       }
     ]
};
if (contact.PermissionToCall)
{
  alert("Call " + contact.Name + " at " + contact.PhoneNumbers[0].Number);
}
  

注意如需 JavaScript 常值支援的詳細討論,請參閱常值一節下的Core JavaScript 1.5 指南

從 JavaScript 常值到 JSON

JSON 是從 JavaScript 中常值物件標記法子集建立的資料交換格式。 雖然 JavaScript 針對常值所接受的語法非常有彈性,但請務必注意 JSON 具有更嚴格的規則。 例如,根據 JSON 標準,物件成員的名稱 必須是 有效的 JSON 字串。 JSON 中的字串 必須 以引號括住。 另一方面,JavaScript 允許以引號或單引號分隔物件成員名稱,或只要成員名稱與保留的 JavaScript 關鍵字不衝突,就可以省略引號。 同樣地,JSON 中的陣列元素或物件成員值限制為非常有限的集合。 不過,在 JavaScript 中,陣列元素和物件成員值可以參考幾乎所有有效的 JavaScript 運算式,包括函式呼叫和定義!

JSON 的特色在於其簡單性。 根據 JSON 標準格式化的訊息是由單一最上層物件或陣列所組成。 陣列元素和物件值可以是物件、陣列、字串、數位、布林值 (true 和 false) 或 null。 總而言之,這是 JSON 標準! 真的就是這麼簡單。 如需標準更正式的描述,請參閱 www.json.orgRFC 4627

JSON 的其中一個重點是缺少日期/時間常值。 許多人在第一次遇到 JSON 時感到驚奇且感到感到滿意。 缺少日期/時間常值的簡單說明 (解決或未) ,就是 JavaScript 從未有過:JavaScript 中的日期和時間值支援完全透過 Date 物件提供。 大部分使用 JSON 作為資料格式的應用程式,因此通常會使用字串或數位來表示日期和時間值。 如果使用字串,您通常會預期它採用 ISO 8601 格式。 如果改用數位,則值通常會採用表示自 epoch 起的國際標準時間 (UTC) 中的毫秒數,其中 epoch 定義為 1970 年 1 月 1 日午夜 (UTC) 。 同樣地,這只是慣例,而不是 JSON 標準的一部分。 如果您要與另一個應用程式交換資料,則必須檢查其檔,以瞭解它如何編碼 JSON 常值內的日期和時間值。 例如,Microsoft 的 ASP.NET AJAX 不會使用上述任一慣例。 相反地,它會將 .NET DateTime 值編碼為 JSON 字串,其中字串的內容是 \/Date (刻度) \/ ,而 刻度 代表自 epoch (UTC) 以來的毫秒數。 因此,1989 年 11 月 29 日上午 4:55:30,UTC 編碼為 「\/Date (628318530718) \/」。 For some rationale behind this rather contrived choice of encoding, see "Inside ASP.NET AJAX’s JSON date and time string."

比較 JSON 與 XML

JSON 和 XML 都可以用來以文字為基礎的人類可讀資料交換格式來表示原生、記憶體內建物件。 此外,這兩種資料交換格式為等態,其中一種格式的指定文字,另一種格式是相等的。 例如,呼叫其中一個 Yahoo!可公開存取的 Web 服務時,您可以透過 querystring 參數來指出回應是否應該格式化為 XML 或 JSON。 因此,在決定資料交換格式時,選擇一個格式而非另一個做為銀級專案符號並不簡單,而是哪些格式具有讓其成為特定應用程式最佳選擇 的特性 。 例如,XML 在標記檔文字方面具有其根,而且通常會在該空間 (中表現得非常清楚,因為 XHTML) 很明顯。 另一方面,JSON 在程式設計語言類型和結構中具有其根目錄,因此提供更自然且立即可用的對應來交換結構化資料。 除了這兩個起點之外,下表將協助您瞭解及比較 XML 和 JSON 的主要特性。

XML 與 JSON 之間的主要特性差異

特性 XML JSON
資料類型 不提供資料類型的任何概念。 其中一個必須依賴 XML 架構 來新增類型資訊。 提供純量資料類型,以及透過陣列和物件表示結構化資料的能力。
支援陣列 陣列必須透過慣例來表示,例如透過使用外部預留位置元素,將陣列內容模型化為內部元素。 一般而言,專用項目會使用用於內部元素之名稱的複數形式。 原生陣列支援。
支持對象 物件必須透過慣例來表示,通常是透過混合使用屬性和元素。 原生物件支援。
Null 支援 需要在 XML 實例檔中的元素上使用 xsi:nil ,加上對應命名空間的匯入。 原生方式會辨識 Null 值。
註解 原生支援,且通常可透過 API 取得。 不支援。
命名空間 支援命名空間,這可消除合併檔時名稱衝突的風險。 命名空間也允許安全地擴充現有的 XML 型標準。 命名空間沒有概念。 命名衝突通常是藉由巢狀物件或在物件成員名稱中使用前置詞來避免, (前者在實務上是慣用的) 。
格式化決策 複雜。 需要更努力,才能決定如何將應用程式類型對應至 XML 元素和屬性。 可以建立元素中心或以屬性為中心的方法是否更好, 簡單。 為應用程式資料提供更直接的對應。 唯一的例外狀況可能是沒有日期/時間常值。
大小 檔的大小通常很冗長,特別是使用以元素為中心的格式方法時。 語法非常簡潔,而且會產生格式化的文字,其中大部分的空間會正確取用 (,因此會由表示的資料) 。
在 JavaScript 中剖析 需要 XML DOM 實作和其他應用程式程式碼,才能將文字對應回 JavaScript 物件。 剖析文字不需要其他應用程式程式碼;可以使用 JavaScript 的 eval 函式。
学习曲线 通常通常需要使用數種技術: XPath、XML 架構、 XSLTXML 命名空間DOM等等。 開發人員已熟悉 JavaScript 或其他動態程式設計語言的背景技術堆疊。

JSON 是相對新的資料交換格式,而且目前沒有 XML 享有的採用或廠商支援, (雖然 JSON 快速趕上) 。 下表醒目提示 XML 和 JSON 空間中目前事務的狀態。

支援 XML 與 JSON 之間的差異

支援 XML JSON
工具 享受許多產業廠商廣泛使用的一組成熟工具。 豐富的工具支援,例如編輯器和格式器。
Microsoft .NET Framework 自 .NET Framework 1.0 版起,非常良好且成熟支援。 XML 支援可做為基類庫的一部分, (BCL) 。 針對非受控環境,有 MSXML。 到目前為止,除了初始實作作為 AJAX ASP.NET一部分以外。
平臺和語言 剖析器和格式器在許多平臺和語言上廣泛使用, (商業和開放原始碼實作) 。 剖析器和格式器已在許多平臺上和多種語言提供。 如需一組良好的參考 ,請參閱 json.org 。 目前大部分的實作通常會開放原始碼專案。
整合式語言 產業廠商目前正在試驗 語言內的支援 。 如需詳細資訊 ,請參閱 Microsoft 的 LINQ 專案 只有 JavaScript/ECMAScript 才支援原生支援。

注意 這兩個數據表都不是完整的比較點清單。 有進一步的角度可以比較這兩種資料格式,但我們覺得這些重點應該足以建立初始印象。

使用 JavaScript 建立和剖析 JSON 訊息

使用 JSON 作為資料交換格式時,兩個常見的工作是將原生和記憶體內部表示轉換成其 JSON 文字標記法,反之亦然。 不幸的是,在撰寫時,JavaScript 不會提供內建函式,以從指定的物件或陣列建立 JSON 文字。 這些方法應該包含在 2007 年第四版的 ECMAScript 標準中。 在正式新增這些 JSON 格式函式至 JavaScript 並廣泛適用于熱門實作之前,請使用可在 下載 http://www.json.org/json.js 的參考實作腳本。

在撰寫本文時的最新反覆運算中, www.json.orgjson.js 腳本會新增至陣列、字串、布林值、物件和其他 JavaScript 類型的 JSONString () 函式。 純量類型的 toJSONString () 函式 (Number 和 Boolean) 相當簡單,因為它們只需要傳回實例值的字串表示。 例如,布林類型的toJSONString () 函式會傳回字串 「true」,否則會傳回字串 「true」,否則傳回 「false」。 Array 和 Object 類型的 toJSONString () 函式更有趣。 針對 Array 實例,會依序呼叫每個自主專案的 toJSONString () 函式,並以逗號串連結果來分隔每個結果。 以方括弧括住的最終輸出。 同樣地,針對 Object 實例,會列舉每個成員,並叫用其 toJSONString () 函式。 成員名稱和其值的 JSON 標記法會與中間的冒號串連;每個成員名稱和值組會以逗號分隔,而且整個輸出會以大括弧括住。

toJSONString () 函式的淨結果是,任何類型都可以使用單一函式呼叫轉換成其 JSON 格式。 下列 JavaScript 會建立 Array 物件,並刻意使用詳細資訊和非常值方法新增七個 String 元素,以供說明之用。 然後,它會繼續顯示陣列 JSON 標記法:

  
    // josn.js must be included prior to this point

var continents = new Array();
continents.push("Europe");
continents.push("Asia");
continents.push("Australia");
continents.push("Antarctica");
continents.push("North America");
continents.push("South America");
continents.push("Africa");

alert("The JSON representation of the continents array is: " +
 continents.toJSONString());
  

Bb299886.intro_to_json01 (en-us,MSDN.10) .gif

圖 1. toJSONString () 函式會根據 JSON 標準發出格式化的陣列。

剖析 JSON 文字甚至更簡單。 因為 JSON 只是 JavaScript 常值的子集,所以可以使用eval (expr) 函式, 將來源 JSON 文字視為 JavaScript 原始程式碼,剖析成記憶體內部標記法。 eval函式接受作為有效 JavaScript 程式碼字串的輸入,並評估運算式。 因此,下列單行程式碼只需要將 JSON 文字轉換成原生標記法:

  
    var value = eval( "(" + jsonText + ")" );
  

注意 使用額外的括弧會使 eval 無條件地將來源輸入視為運算式。 對於 物件而言,這特別重要。 如果您嘗試使用包含定義物件的 JSON 文字來呼叫 eval,例如字串 「 {} 」 (表示空物件) ,則只會傳回未定義為剖析的結果。 括弧會強制 JavaScript 剖析器看到最上層大括弧做為物件實例的常值標記法,例如大括弧定義語句區塊。 此外,如果最上層專案是陣列,則不會發生相同的問題,如同在 eval (「[1,2,3]」) 中。 不過,為了一致起見,在呼叫 eval 之前,應該一律以括弧括住 JSON 文字,如此一來,如何解譯來源就沒有模棱兩可的情況。

評估常值標記法時,會傳回對應至常值語法的實例,並指派給 。 請考慮下列範例,它會使用 eval 函數來剖析陣列的常值標記法,並將產生的陣列指派給變數 大陸

  
    var arrayAsJSONText = '["Europe", "Asia", "Australia", "Antarctica",
 "North America", "South America", "Africa"]';
var continents = eval( arrayAsJSONText );
alert(continents[0] + " is one of the " + continents.length + "
 continents.");
  

當然,在實務上,評估的 JSON 文字會來自某些外部來源,而不是硬式編碼,如上述案例所示。

eval函式會以盲目方式評估傳遞的任何運算式。 因此,不受信任的來源可能包含潛在的危險 JavaScript,以及或混合到構成 JSON 資料的常值標記法中。 在無法信任來源的情況下,強烈建議您使用 parseJSON () 函式剖析 JSON 文字, (在json.js) 中找到:

  
    // Requires json.js
var continents = arrayAsJSONText.parseJSON();
  

parseJSON () 函式也會使用eval,但只有在arrayAsJSONText中包含的字串符合 JSON 文字標準時。 它會使用聰明的正則運算式測試來執行這項作業。

在.NET Framework中使用 JSON

JSON 文字可以輕鬆地從 JavaScript 程式碼建立和剖析,這是其一部分。 不過,當 JSON 用於 ASP.NET Web 應用程式中時,只有瀏覽器會享有 JavaScript 支援,因為伺服器端程式碼最有可能以 Visual Basic 或 C# 撰寫。

大部分專為 ASP.NET 設計的 Ajax 程式庫都支援以程式設計方式建立和剖析 JSON 文字。 因此,若要在 .NET 應用程式中使用 JSON,請考慮使用其中一個程式庫。 有許多開放原始碼和協力廠商選項,而 Microsoft 也有自己的 Ajax 程式庫 ,ASP.NET AJAX

在本文中,我們將探討使用Jayrock的範例,這是共同撰寫Atif Aziz所建立之 Microsoft .NET Framework JSON 的開放原始碼實作。 基於下列三個原因,我們選擇使用 Jayrock 而不是 ASP.NET AJAX:

  • Jayrock 是開放原始碼,因此您可以視需要擴充或自訂。
  • Jayrock 可用於 ASP.NET 1.x、2.0 和 Mono 應用程式,而 ASP.NET AJAX 僅適用于 ASP.NET 2.0 版。
  • Jayrock 的範圍僅限於 JSON 和 JSON-RPC,而前者是本文的主要焦點。 雖然 ASP.NET AJAX 包含建立和剖析 JSON 文字的一些支援,但其主要目的是提供豐富的平臺,以在 ASP.NET 中建置端對端 Ajax 樣式的 Web 應用程式。 當您的主要焦點是 JSON 時,額外的鈴鐺和門道可能會干擾。

使用 Jayrock 在 .NET 中使用 JSON 類似于透過 .NET Framework 中的XmlWriterXmlReaderXmlSerializer類別來處理 XML。 在 Jayrock 中找到的JsonWriterJsonReaderJsonTextWriterJsonTextReader類別會模擬 .NET Framework xmlWriterXmlReaderXmlTextWriterXmlTextReader類別的語意。 這些類別適用于在低層級和資料流程導向層級與 JSON 互動。 您可以使用這些類別,透過一系列的方法呼叫來建立或剖析 JSON 文字。 例如,使用 JsonWriter 類別方法 WriteNumber (number) 根據 JSON 標準寫出適當的 數位 字串表示。 JsonConvert類別提供匯出入方法,可在 .NET 類型和 JSON 之間轉換。 這些方法提供類似 XmlSerializer 類別方法 SerializeDeserialize中的類似功能。

建立 JSON 文字

下列程式碼說明如何使用 JsonTextWriter 類別來建立大陸字串陣列的 JSON 文字。 此 JSON 文字會傳送至傳遞至建構函式的 TextWriter 實例,此範例中的輸出資料流程會發生在此範例中 (,ASP.NET 您可以使用 Response.Output,改為使用 Response.Output) :

  
    using (JsonTextWriter writer = JsonTextWriter(Console.Out))
{
    writer.WriteStartArray();
    writer.WriteString("Europe");
    writer.WriteString("Asia");
    writer.WriteString("Australia");
    writer.WriteString("Antarctica");
    writer.WriteString("North America");
    writer.WriteString("South America");
    writer.WriteString("Africa");
    writer.WriteEndArray();
}
  

除了 WriteStartArrayWriteStringWriteEndArray 方法之外, JsonWriter 類別還提供撰寫其他 JSON 實數值型別的方法,例如 WriteNumberWriteBooleanWriteNull等等。 WriteStartObjectWriteEndObjectWriteMember方法會建立物件的 JSON 文字。 下列範例說明如何為 JavaScript 中的「瞭解 JavaScript 中的常值標記法」一節中檢查的連絡人物件建立 JSON 文字:

private static void WriteContact()
{
    using (JsonWriter w = new JsonTextWriter(Console.Out))
    {
        w.WriteStartObject();              // {
        w.WriteMember("Name");             //   "Name" : 
        w.WriteString("John Doe");         //     "John Doe",
        w.WriteMember("PermissionToCall"); //   "PermissionToCall" :
        w.WriteBoolean(true);              //     true,
        w.WriteMember("PhoneNumbers");     //   "PhoneNumbers" :
        w.WriteStartArray();               //   [ 
        WritePhoneNumber(w,                //     { "Location": "Home",
            "Home"                         //       "Number": 
            "555-555-1234");               //         "555-555-1234" },
        WritePhoneNumber(w,                //     { "Location": "Work",
            "Work",                        //       "Number": 
            "555-555-9999");               //       "555-555-9999" }
        w.WriteEndArray();                 //   ]
        w.WriteEndObject();                // }
    }
}

private static void WritePhoneNumber(JsonWriter w, string location,
    string number)
{
    w.WriteStartObject();      //  {
    w.WriteMember("Location"); //      "Location" : 
    w.WriteString(location);   //          "...", 
    w.WriteMember("Number");   //      "Number" :
    w.WriteString(number);     //          "..."
    w.WriteEndObject();        //  }
}

JsonConvert類別中的匯出ExportToString方法可用來將指定的 .NET 類型序列化為 JSON 文字。 例如,使用 JsonTextWriter 類別來手動建置七大陸陣列的 JSON 文字,下列 JsonConvert.ExportToString 呼叫會產生相同的結果:

string[] continents = {
      "Europe", "Asia", "Australia", "Antarctica", "North America", 
      "South America", "Africa"
};
string jsonText = JsonConvert.ExportToString(continents);

剖析 JSON 文字

JsonTextReader類別提供各種方法來剖析 JSON 文字的權杖,其中的核心為Read。 每次叫用 Read 方法時,剖析器都會取用下一個權杖,可能是字串值、數位值、物件成員名稱、陣列開頭等等。 如果適用,可以透過 Text 屬性存取目前權杖的剖析 文字 。 例如,如果讀取器位於布林資料上, 則 Text 屬性會根據實際的剖析值傳回 「true」 或 「false」。

下列範例程式碼會使用 JsonTextReader 類別來剖析字串陣列的 JSON 文字標記法,其中包含七大陸的名稱。 以字母 「A」 開頭的每個大陸都會傳送至主控台:

  
    string jsonText = @"[""Europe"", ""Asia"", ""Australia"", ""Antarctica"",
 ""North America"", ""South America"", ""Africa""]";

using (JsonTextReader reader = new JsonTextReader(new
 StringReader(jsonText)))
{
    while (reader.Read())
    {
        if (reader.TokenClass == JsonTokenClass.String &&
            reader.Text.StartsWith("A"))
        {
            Console.WriteLine(reader.Text);
        }
    }
}
  

注意 Jayrock 中的 JsonTextReader 類別是相當寬鬆的 JSON 文字剖析器。 它實際上允許比根據 RFC 4627中所配置的規則,視為有效的 JSON 文字更多的語法。 例如, JsonTextReader 類別可讓單行和多行批註出現在 JSON 文字中,如同您在 JavaScript 中預期一樣。 單行批註開頭為斜線 () // 和多行批註,並以斜線star (/*) 結尾,並以斜線 () star */ 結尾。 單行批註甚至可以以雜湊/磅號開頭 () # ,這是 Unix 樣式組態檔中常見的。 在所有實例中,剖析器會完全略過批註,且永遠不會透過 API 公開。 如同在 JavaScript 中, JsonTextReader 允許 JSON 字串以單引號分隔 (') 。 剖析器甚至可以容許在陣列之物件或元素的最後一個成員後面加上額外的逗號。

即使是所有這些新增專案, JsonTextReader 也是一個符合規範的剖析器! 另一方面,JsonTextWriter只會產生嚴格符合標準的 JSON 文字。 這會遵循通常作為強固性主體所產生的內容,其指出「在您執行的動作中保持保守;對其他人接受的內容有一些心力。」

若要直接將 JSON 文字轉換成 .NET 物件,請使用 JsonConvert 類別匯入方法,指定輸出類型和 JSON 文字。 下列範例顯示將字串的 JSON 陣列轉換成 .NET 字串陣列:

string jsonText = @"[""Europe"", ""Asia"", ""Australia"", ""Antarctica"",
 ""North America"", ""South America"", ""Africa""]";

string[] continents = (string[]) JsonConvert.Import(typeof(string[]),
 jsonText);

以下是採用 RSS XML 摘要、使用 XmlSerializer將它還原序列化為 .NET 類型的更有趣轉換範例,然後使用 JsonConvert 將物件轉換成 JSON 文字, (有效地將 XML 中的 RSS 轉換成 JSON 文字) :

XmlSerializer serializer = new XmlSerializer(typeof(RichSiteSummary));
RichSiteSummary news;

// Get the MSDN RSS feed and deserialize it...

using (XmlReader reader = XmlReader.Create("https://msdn.microsoft.com/rss.xml"))
    news = (RichSiteSummary) serializer.Deserialize(reader);

// Export the RichSiteSummary object as JSON text, emitting the output to
// Console.Out.

using (JsonTextWriter writer = new JsonTextWriter(Console.Out))
    JsonConvert.Export(news, writer);

注意RichSiteSummary的定義及其相關類型可以在本文隨附的範例中找到。

在 ASP.NET 中使用 JSON

查看在 JavaScript 中使用 JSON 的方法,以及從使用 Jayrock 的.NET Framework內使用 JSON 的方式,現在可以變成實際範例,說明如何套用所有這些知識。 請考慮 ASP.NET 2.0 中的用戶端腳本回呼功能,以簡化從網頁瀏覽器對 ASP.NET 網頁進行頻外呼叫的程式 (或頁面) 的特定控制項。 在典型的回呼案例中,瀏覽器中的用戶端腳本會封裝,並將資料傳回 Web 服務器,以供伺服器端方法處理。 從伺服器接收回應資料之後,用戶端接著會使用它來更新瀏覽器顯示。

注意 如需詳細資訊,請參閱 MSDN Magazine 文章 ASP.NET 2.0 中的腳本回呼

用戶端回呼案例中的挑戰是用戶端和伺服器只能來回寄送字串。 因此,要交換的資訊必須從原生的記憶體內部表示轉換成字串,然後從字串剖析回接收時的原生記憶體內部標記法。 ASP.NET 2.0 中的用戶端腳本回呼功能不需要交換資料的特定字串格式,也不會提供任何內建功能,以在原生記憶體中和字串表示之間轉換;開發人員必須根據自己選擇的一些資料交換格式來實作轉換邏輯。

下列範例說明如何在用戶端腳本回呼案例中使用 JSON 作為資料交換格式。 特別是,此範例包含 ASP.NET 網頁,該頁面使用 Northwind 資料庫的資料來提供下拉式清單中的類別清單;選取類別中的產品會顯示在項目符號清單中, (請參閱圖 3) 。 每當用戶端上的下拉式清單變更時,就會傳回回呼,其單一元素是選取的 CategoryID陣列。

注意 我們會傳入陣列,其中包含選取的 CategoryID 做為其唯一元素 (,而不只是 CategoryID) ,因為 JSON 標準要求任何 JSON 文字都必須有物件或陣列做為其根目錄。 當然,用戶端不需要將 JSON 文字傳遞至伺服器,我們可以讓此範例只傳遞選取的 CategoryID 作為字串。 不過,我們想要示範在回呼的要求和回應訊息中傳送 JSON 文字。

Page_Load事件處理常式中的下列程式碼會設定Categories DropDownList Web 控制項,以便在變更時呼叫GetProductsForCategory函式,並傳遞選取的下拉式清單值。 如果傳入的下拉式清單值大於零,此函式會起始用戶端腳本回呼:

  
    // Add client-side onchange event to drop-down list
Categories.Attributes["onchange"] = "Categories_onchange(this);";

// Generate the callback script
string callbackScript = ClientScript.GetCallbackEventReference(
    /* control        */ this, 
    /* argument       */ "'[' + categoryID + ']'", 
    /* clientCallback */ "showProducts", 
    /* context        */ "null");

// Add the Categories_onchange function
ClientScript.RegisterClientScriptBlock(GetType(),
"Categories_onchange", @"
    function Categories_onchange(sender)
    {
        clearResults();

        var categoryID = sender.value;            
        if (categoryID > 0)
        {
            " + callbackScript + @"
        }
    }", true);
  

ClientScriptManager類別中的GetCallBackEventReference方法,用來產生叫用回呼的 JavaScript 程式碼,具有下列簽章:

  
    public string GetCallbackEventReference (
    Control control,
    string argument,
    string clientCallback,
    string context,
)
  

引數參數會指定在回呼期間從用戶端傳送至 Web 服務器的資料,而 clientCallback參數會指定在回呼完成時叫用的用戶端函式名稱, (showProducts) 。 GetCallBackEventReference方法呼叫會產生下列 JavaScript 程式碼,並將它新增至轉譯的標記:

  
    WebForm_DoCallback('__Page','[' + categoryID + 
']',showProducts,null,null,false)
  

'[' + categoryID + ']' 是在回呼期間傳遞至伺服器的值, (具有單一元素、 categoryID) 且 showProducts 是回呼傳回時執行的 JavaScript 函式。

在伺服器端,回應回呼時所執行的方法會使用 Jayrock 的 JsonConvert 類別來剖析傳入 JSON 文字並格式化傳出 JSON 文字。 特別是,會擷取與所選類別相關聯的產品名稱,並以字串陣列的形式傳回。

  
    // Deserialize the JSON text into an array of integers
int[] args = (int[]) JsonConvert.Import(typeof(int[]), eventArgument);

// Read the selected CategoryID from the array
int categoryID = args[0];

// Get products based on categoryID 

  NorthwindDataSet.ProductsRow[] rows = 
Northwind.Categories.FindByCategoryID(categoryID).GetProductsRows();

// Load the names into a string array
string[] productNames = new string[rows.Length];
for (int i = 0; i < rows.Length; i++)
{
    productNames[i] = rows[i].ProductName;
}

// Serialize the string array as JSON text and return it to the client
return JsonConvert.ExportToString(productNames);

注意JsonConvert類別會使用兩次,一次將eventArgument中的 JSON 文字轉換成整數陣列,然後將字串陣列productNames轉換成 JSON 文字,以返回用戶端。 或者,我們可以在這裡使用JsonReader 和 JsonWriter類別,但JsonConvert會在涉及的資料相對小且輕鬆地對應至現有類型時,執行相同的作業。

從伺服器端傳回資料時,會呼叫 GetCallBackEventReference 方法所指定的 JavaScript 函式,並傳遞傳回值。 這個 JavaScript 方法showProducts是從參考< div >元素ProductOutput開始。 然後,它會剖析 JSON 回應,並動態新增一個未排序的清單,其中包含每個陣列元素的清單專案。 如果未針對選取的類別傳回任何產品,則會改為顯示對應的訊息。

function showProducts(arg, context)
{
    // Dump the JSON text response from the server.

    document.forms[0].JSONResponse.value = arg;
    
    // Parse JSON text returned from callback.

    var categoryProducts = eval("(" + arg + ")");

    // Get a reference to the <div> ProductOutput.
    
    var output = document.getElementById("ProductOutput");

    // If no products for category, show message.
    
    if (categoryProducts.length == 0)
    {
        output.appendChild(document.createTextNode(
            "There are no products for this category..."));
    }
    else
    {
        // There are products, display them in an unordered list. 
        
        var ul = document.createElement("ul");
        
        for (var i = 0; i < categoryProducts.length; i++)
        {
            var product = categoryProducts[i];
            var li = document.createElement("li");
            li.appendChild(document.createTextNode(product));
            ul.appendChild(li);
        }
        
        output.appendChild(ul);
    }
}

圖 2 說明事件順序,而圖 3 顯示此範例的運作方式;本文下載包含完整的程式碼。

Bb299886.intro_to_json02 (en-us,MSDN.10) .gif

圖 2:用戶端會將選取的 CategoryID 傳送為數組中的單一元素,而伺服器會傳回相關聯產品名稱的陣列。

Bb299886.intro_to_json03 (en-us,MSDN.10) .gif

圖 3:產品會顯示在所選類別內的項目符號清單中。

結論

JSON 是以 JavaScript 程式設計語言中常值標記法子集為基礎的輕量型文字型資料交換格式。 它為應用程式資料結構提供簡潔編碼,通常用於 JavaScript 實作可供一或兩個應用程式交換資料的案例使用,例如 Ajax 樣式 Web 應用程式中。 JSON 的運用在於其簡單易懂、採用及實作。 JSON 對於已經熟悉 JavaScript 或其他程式設計語言的開發人員,幾乎沒有任何學習曲線,其支援豐富的常值標記法 (,例如 Python 和 Ruby) 。 只要呼叫 eval 函式,即可在 JavaScript 程式碼中剖析 JSON 文字,而建立 JSON 文字是json.js腳本提供的 http://www.json.org/json.js 一項簡單功能。

在所有主要平臺和架構中,有一些程式庫可用於 JSON。 在本文中,我們已探討 Jayrock,這是一個在 .NET 應用程式中建立和剖析 JSON 文字的開放原始碼程式庫。 Jayrock 可用於 ASP.NET 1.x、2.0 和 Mono 應用程式。 ASP.NET AJAX 提供類似的 JSON 功能,但僅適用于 ASP.NET 2.0 應用程式。

快樂的程式設計!

參考資料

Ajax 或 AJAX?

Ajax 一開始是由 Jesse James Garrett 所建立,用來描述建立高度互動式 Web 應用程式所涉及的 Web 應用程式和一組技術樣式。 在過去,Ajax 一詞會以縮寫 AJAX 的形式散佈在網路上,這表示非同步 JavaScript And XML。 不過,隨著時間,人們發現 AJAX 中的 「X」 並不非常代表用來在背景與網頁伺服器通訊的基礎資料格式,因為大部分的實作都是切換為更簡單且更有效率的替代方案。 因此,比起 AJAJ 這類取代縮寫是一些雙曲子,縮略字通常會淘汰,而不是 Ajax 字詞,而不是 AJAX 縮寫。

在撰寫本文時,預期會看到混合且廣泛的 「AJAX」 和 「Ajax」 用法,以表示一個和相同的專案。 在本文中,我們停滯于「Ajax the term」。不過,提供可啟用 Ajax 樣式應用程式的架構的商業產品,通常會使用縮略字形式來區別類似名稱的清除代理程式產品,並避免任何潛在的商標或法律爭議。

ASP.NET AJAX:JSON 日期和時間字串內部

ASP.NET 中的 AJAX JSON 序列化程式會將 DateTime 實例編碼為 JSON 字串。 在其發行前週期期間,ASP.NET AJAX 使用 「@ticks@」 格式,其中刻度代表自通用國際標準時間 1970 年 1 月 1 日起的毫秒數, (UTC) 。 UTC 中的日期和時間,例如 1989 年 11 月 29 日,上午 4:55:30 會寫出為 「@62831853071@」。雖然簡單且簡單,但此格式無法區分序列化的日期和時間值,以及看起來像序列化日期但並非要還原序列化的字串。 因此,ASP.NET AJAX 小組會採用 「\/Date (刻度) \/」 格式,變更最終版本以解決此問題。

新的格式依賴小型技巧來降低錯誤解譯的機會。 在 JSON 中,字串中的正斜線 (/) 字元可以使用反斜線 \ () 逸出,即使它並非完全必要也一樣。 利用這項功能,ASP.NET AJAX 小組修改了 JavaScriptSerializer,將 DateTime 實例寫入為字串 「\/Date (ticks) \/」。 兩個正斜線的逸出是超級的,但對 JavaScriptSerializer 而言很重要。 根據 JSON 規則, "\/Date (刻度) \/" 的技術相當於 "/Date (刻度) /," 但 JavaScriptSerializer 會將前者還原序列化為DateTime,而後者則是字串。 因此,相較于發行前版本更簡單的「@ticks@」格式,模棱兩可的機會會大幅減少。

特別感謝

將本文提交至 MSDN 之前,我們有一些自願人員協助校訂文章,並提供內容、文法和方向的意見反應。 檢閱程式的主要參與者包括一些「密利克 Crockford」、Eric Schönholzer 和「密拉夫」Negovan。

關於作者

Atif Aziz 是 Skybow AG 的主要顧問,其主要重點是協助客戶瞭解並建置 .NET 開發平臺上的解決方案。 Atif 會定期參與 Microsoft 開發人員社群,方法是在會議和撰寫技術出版物的文章。 他是最大的瑞士 .NET 使用者群組的 INETA 演講者和長。 他可以在 或 透過位於 的網站 http://www.raboof.com 來連絡 atif.aziz@skybow.com

Scott Mitchell 是六部 ASP/ASP.NET 書籍的作者,4GuysFromRolla.com 的作者,自 1998 年以來,已與 Microsoft Web 技術合作。 Scott 是獨立顧問、訓練員和作者。 他可以透過 mitchell@4guysfromrolla.com 其部落格或透過其部落格來連線: http://ScottOnWriting.net