Общие сведения о нотации объектов JavaScript (JSON) в JavaScript и .NET

 

Общие сведения о нотации объектов JavaScript (JSON) в JavaScript и .NET

Atif Aziz, Скотт Митчелл

Февраль 2007 г.

Область применения:
   JSON
   Ajax

Сводка: В этой статье рассматривается нотация объектов JavaScript (или JSON), открытый и текстовый формат обмена данными, который предоставляет стандартизированный формат обмена данными, который лучше подходит для веб-приложений в стиле Ajax. (22 печатных страниц)

Содержимое

Введение
Общие сведения о литеральной нотации в JavaScript
Сравнение JSON и XML
Создание и анализ сообщений JSON с помощью JavaScript
Работа с JSON в платформа .NET Framework
Заключение
Ссылки

Скачайте исходный код этой статьи.

Введение

При проектировании приложения, которое будет взаимодействовать с удаленным компьютером, необходимо выбрать формат данных и протокол обмена. Существует множество открытых, стандартизированных вариантов, и идеальный выбор зависит от требований приложений и существующих функций. Например, веб-службы на основе SOAP форматируют данные в полезные данные XML, заключенные в конверт SOAP.

Хотя XML хорошо подходит для многих сценариев приложений, он имеет некоторые недостатки, которые делают его менее идеальным для других. Одним из таких областей, где XML часто меньше идеала, является использование веб-приложений в стиле Ajax. Ajax — это метод, используемый для создания интерактивных веб-приложений, которые обеспечивают более удобный пользовательский интерфейс за счет использования внешних и упрощенных вызовов веб-сервера вместо обратной передачи на всю страницу. Эти асинхронные вызовы инициируются на клиенте с помощью JavaScript и включают форматирование данных, их отправку на веб-сервер, анализ и работу с возвращаемыми данными. Хотя большинство браузеров могут создавать, отправлять и анализировать XML, нотация объектов JavaScript (или JSON) предоставляет стандартизированный формат обмена данными, который лучше подходит для веб-приложений в стиле Ajax.

JSON — это открытый текстовый формат обмена данными (см. RFC 4627). Как и XML, он является удобочитаемым, независимым от платформы и имеет широкий спектр реализаций. Данные, отформатированные в соответствии со стандартом JSON, являются упрощенными и могут быть проанализированы реализациями JavaScript с невероятной легкостью, что делает их идеальным форматом обмена данными для веб-приложений Ajax. Так как это в основном формат данных, JSON не ограничивается только веб-приложениями Ajax и может использоваться практически в любом сценарии, где приложения должны обмениваться структурированной информацией в виде текста или хранить их.

В этой статье рассматривается стандарт JSON, его связь с JavaScript и его сравнение с XML. Обсуждается jayrock, реализация JSON с открытым кодом для .NET, а примеры создания и анализа сообщений JSON приведены в JavaScript и C#.

Общие сведения о литеральной нотации в JavaScript

Литералы используются в языках программирования для буквального выражения фиксированных значений, таких как константное целочисленное значение 4 или строка "Hello, World". Литералы можно использовать в большинстве языков, где разрешено выражение, например часть условия в операторе управления, входной параметр при вызове функции, назначение переменных и т. д. Например, следующий код C# и Visual Basic инициализирует переменную x с константным целым числом 42.

  
    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";
  

Объектный литерал определяет элементы объекта и их значения. Список элементов и значений объекта заключен в фигурные скобки ({}), и каждый элемент отделяется запятой. В каждом элементе имя и значение разделяются двоеточием (:). В следующем примере создается объект и инициализируется его тремя элементами с именами Address, City и PostalCode с соответствующими значениями "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.org или RFC 4627 .

Одной из точек JSON является отсутствие литерала даты и времени. Многие люди удивлены и разочарованы, узнав это, когда они впервые сталкиваются с JSON. Простое объяснение (утешающее или нет) отсутствия литерала даты и времени заключается в том, что в JavaScript его никогда не было: поддержка значений даты и времени в JavaScript полностью предоставляется через объект Date . Поэтому большинство приложений, использующих JSON в качестве формата данных, обычно используют строку или число для выражения значений даты и времени. Если используется строка, обычно ее можно ожидать в формате ISO 8601. Если вместо этого используется число, то значение обычно принимается как число миллисекундах в формате UTC с момента эпохи, где эпоха определяется как полночь 1 января 1970 года (UTC). Опять же, это просто соглашение, а не часть стандарта JSON. При обмене данными с другим приложением необходимо проверка его документацию, чтобы узнать, как кодируются значения даты и времени в литерале JSON. Например, в ASP.NET AJAX корпорации Майкрософт не используется ни то из описанных соглашений. Вместо этого он кодирует значения .NET DateTime в виде строки JSON, где содержимое строки \/Date(ticks)\/ и где деления представляют миллисекунды с эпохи (UTC). Поэтому 29 ноября 1989 г., 4:55:30 в формате UTC кодируется как "\/Date(628318530718)\/". Некоторые обоснования этого довольно надуманный выбор кодирования см. в разделе "Внутри ASP.NET json строки даты и времени AJAX".

Сравнение JSON и XML

Json и XML можно использовать для представления собственных объектов в памяти в текстовом, удобочитаемом формате обмена данными. Кроме того, два формата обмена данными являются изоморфными: при наличии текста в одном формате эквивалентный формат можно представить в другом. Например, при вызове одной из общедоступных веб-служб Yahoo! с помощью параметра querystring можно указать, должен ли ответ быть отформатирован как XML или JSON. Таким образом, при выборе формата обмена данными не просто выбрать один из них в качестве серебряной пули, а скорее, какой формат имеет характеристики , которые делают его оптимальным для конкретного приложения. Например, XML имеет свои корни в тексте документа маркировки и, как правило, очень хорошо светится в этом пространстве (как это видно в XHTML). JSON, с другой стороны, имеет свои корни в типах и структурах языка программирования и, следовательно, обеспечивает более естественное и доступное сопоставление для обмена структурированными данными. Помимо этих двух отправных точек, следующая таблица поможет вам понять и сравнить ключевые характеристики XML и JSON.

Основные различия между XML и JSON

Характеристика XML JSON
Типы данных Не содержит понятия о типах данных. Для добавления сведений о типе необходимо полагаться на схему XML . Предоставляет скалярные типы данных и возможность выражения структурированных данных с помощью массивов и объектов.
Поддержка массивов Массивы должны быть выражены соглашениями, например с помощью внешнего элемента-заполнителя, который моделирует содержимое массивов как внутренние элементы. Как правило, внешний элемент использует во множественном числе имя, используемое для внутренних элементов. Поддержка собственных массивов.
Поддержка объектов Объекты должны быть выражены соглашениями, часто через смешанное использование атрибутов и элементов. Поддержка собственных объектов.
Поддержка значений NULL Требуется использование xsi:nil для элементов в документе экземпляра XML, а также импорт соответствующего пространства имен. Изначально распознает значение NULL .
Комментарии Встроенная поддержка и, как правило, доступна через API. Не поддерживается.
Пространства имен Поддерживает пространства имен, что исключает риск конфликтов имен при объединении документов. Пространства имен также позволяют безопасно расширять существующие стандарты на основе XML. Отсутствие концепции пространств имен. Конфликтов именования обычно можно избежать путем вложения объектов или использования префикса в имени члена объекта (первый вариант предпочтительнее на практике).
Решения по форматированию Комплекс. Требует больших усилий, чтобы решить, как сопоставить типы приложений с XML-элементами и атрибутами. Может создавать горячие споры о том, является ли лучше подход, ориентированный на элемент или атрибут. Просто. Обеспечивает гораздо более прямое сопоставление данных приложения. Единственным исключением может быть отсутствие литерала даты и времени.
Размер Документы, как правило, имеют длинный размер, особенно если используется элементно-ориентированный подход к форматированию. Синтаксис очень емкий и дает форматированный текст, где большая часть пространства потребляется (правильно) представленными данными.
Синтаксический анализ в JavaScript Требуется реализация XML DOM и дополнительный код приложения для сопоставления текста с объектами JavaScript. Для анализа текста не требуется дополнительный код приложения; может использовать функцию eval JavaScript.
Период обучения Как правило, требует совместного использования нескольких технологий: XPath, схемы XML, XSLT, пространств имен XML, модели DOM и т. д. Очень простой технологический стек, который уже знаком разработчикам с опытом работы с JavaScript или другими динамическими языками программирования.

JSON является относительно новым форматом обмена данными и не имеет многолетней поддержки или поддержки поставщиков, чем XML пользуется сегодня (хотя JSON быстро догоняет). В следующей таблице показано текущее состояние дел в пространствах XML и JSON.

Различия между XML и JSON

Поддержка XML JSON
Инструменты Пользуется зрелым набором инструментов, широко доступных от многих отраслевых поставщиков. Многофункциональная поддержка средств, таких как редакторы и модули форматирования, ограничена.
Microsoft .NET Framework Очень хорошая и зрелая поддержка с версии 1.0 платформа .NET Framework. Поддержка XML доступна в составе библиотеки базовых классов (BCL). Для неуправляемых сред существует MSXML. Пока нет, за исключением начальной реализации в составе ASP.NET AJAX.
Платформа и язык Средства синтаксического анализа и средства форматирования широко доступны на многих платформах и языках (коммерческие и открытый код реализации). Средства синтаксического анализа и средства форматирования уже доступны на многих платформах и на многих языках. Обратитесь к json.org для получения хорошего набора ссылок. Большинство реализаций сейчас, как правило, открытый код проектов.
Интегрированный язык Отраслевые поставщики в настоящее время экспериментируют с поддержкой буквально в языках. Дополнительные сведения см. в разделе Проект LINQ корпорации Майкрософт . Изначально поддерживается только в JavaScript/ECMAScript.

Примечание Ни одна из таблиц не должна быть исчерпывающим списком точек сравнения. Существуют и другие точки, по которым можно сравнивать оба формата данных, но мы считаем, что этих ключевых моментов должно быть достаточно для создания первоначального впечатления.

Создание и анализ сообщений JSON с помощью JavaScript

При использовании JSON в качестве формата обмена данными две распространенные задачи — преобразование собственного представления и представления в памяти в текстовое представление JSON и наоборот. К сожалению, на момент написания статьи JavaScript не предоставляет встроенных функций для создания текста JSON из заданного объекта или массива. Ожидается, что эти методы будут включены в четвертое издание стандарта ECMAScript в 2007 году. Пока эти функции форматирования JSON не будут официально добавлены в JavaScript и не будут широко доступны в популярных реализациях, используйте скрипт эталонной реализации, доступный для скачивания по адресу http://www.json.org/json.js.

В своей последней итерации на момент написания этой статьи скрипт json.js в www.json.org добавляет функции toJSONString() для массива, строки, логического типа, объекта и других типов JavaScript. Функции toJSONString() для скалярных типов (например, Number и Boolean) довольно просты, так как они должны возвращать только строковое представление значения экземпляра. Например, функция toJSONString() для логического типа возвращает строку "true", если значение равно true, и "false" в противном случае. Функции toJSONString() для типов Array и Object более интересны. Для экземпляров 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 безоговорочно обрабатывал исходные входные данные как выражение. Это особенно важно для объектов . Если попытаться вызвать eval со строкой, содержащей текст JSON, который определяет объект, например строку "{}" (то есть пустой объект), он просто возвращает неопределенный в качестве проанализированного результата. Круглые скобки заставляют средство синтаксического анализа JavaScript видеть фигурные скобки верхнего уровня как литеральную нотацию для экземпляра Object, а не, скажем, фигурные скобки, определяющие блок инструкции. Кстати, такая же проблема не возникает, если элемент верхнего уровня является массивом, как в eval("[1,2,3]")". Однако для единообразия текст JSON всегда должен быть заключен в круглые скобки перед вызовом eval , чтобы не было неоднозначности в интерпретации источника.

При оценке литеральной нотации возвращается экземпляр, соответствующий синтаксису литерала, и присваивается значению. Рассмотрим следующий пример, в котором функция 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. В сценариях, когда источник не может быть доверенным, настоятельно рекомендуется проанализировать текст JSON с помощью функции parseJSON() (находится в json.js):

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

Функция parseJSON() также использует eval, но только в том случае, если строка, содержащаяся в arrayAsJSONText , соответствует стандарту текста JSON. Для этого используется умный тест регулярных выражений.

Работа с JSON в платформа .NET Framework

Текст JSON можно легко создавать и анализировать из кода JavaScript, который является частью его удобства. Однако если JSON используется в веб-приложении ASP.NET, поддержка JavaScript поддерживается только в браузере, так как серверный код, скорее всего, написан на Языке Visual Basic или C#.

Большинство библиотек Ajax, предназначенных для ASP.NET обеспечивают поддержку программного создания и анализа текста JSON. Поэтому для работы с JSON в приложении .NET рассмотрите возможность использования одной из этих библиотек. Существует множество вариантов с открытым исходным кодом и сторонних разработчиков, а корпорация Майкрософт также имеет собственную библиотеку Ajax с именем ASP.NET AJAX.

В этой статье мы рассмотрим примеры использования Jayrock, реализации JSON с открытым кодом для microsoft платформа .NET Framework, созданной соавтором Atif Aziz. Мы решили использовать Jayrock вместо ASP.NET AJAX по трем причинам:

  • Jayrock имеет открытый код, что позволяет расширять или настраивать по мере необходимости.
  • Jayrock можно использовать в приложениях ASP.NET 1.x, 2.0 и Mono , тогда как ASP.NET AJAX предназначен только для ASP.NET версии 2.0.
  • Область Jayrock ограничивается JSON и JSON-RPC, и первый является main фокус этой статьи. Хотя ASP.NET AJAX включает некоторую поддержку для создания и анализа текста JSON, его основная цель — предоставить многофункциональную платформу для создания комплексных веб-приложений в стиле Ajax в ASP.NET. Дополнительные колокольчики и свистки могут отвлекать, когда ваш main фокус — JSON.

Работа с JSON в .NET с помощью Jayrock аналогична работе с XML через классы XmlWriter, XmlReader и XmlSerializer в платформа .NET Framework. Классы JsonWriter, JsonReader, JsonTextWriter и JsonTextReader, найденные в Jayrock, имитируют семантику платформа .NET Framework классов XmlWriter, XmlReader, XmlTextWriter и XmlTextReader. Эти классы полезны для взаимодействия с JSON на низком и потоковом уровнях. С помощью этих классов текст JSON можно создать или проанализировать поэтапно с помощью ряда вызовов методов. Например, при использовании метода класса JsonWriterWriteNumber(number) записывается соответствующее строковое представление числа в соответствии со стандартом JSON. Класс JsonConvert предлагает методы Export и Import для преобразования типов .NET в JSON. Эти методы предоставляют те же функциональные возможности, что и в методах класса XmlSerializerSerialize и Deserialize соответственно.

Создание текста JSON

В следующем коде показано использование класса JsonTextWriter для создания текста JSON для массива строк континентов. Этот текст JSON отправляется в экземпляр TextWriter , передаваемый в конструктор, который в этом примере является потоком вывода из консоли (в ASP.NET вместо него можно использовать 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();
}
  

Помимо методов WriteStartArray, WriteString и WriteEndArray , класс JsonWriter предоставляет методы для записи других типов значений JSON, таких как WriteNumber, WriteBoolean, WriteNull и т. д. Методы WriteStartObject, WriteEndObject и WriteMember создают текст JSON для объекта . В следующем примере показано создание текста JSON для объекта contact, рассмотренного в разделе "Основные сведения об литеральной нотации в JavaScript".

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();        //  }
}

Методы Export и ExportToString в классе JsonConvert можно использовать для сериализации указанного типа .NET в текст JSON. Например, вместо того, чтобы вручную создавать текст JSON для массива семи континентов с помощью класса JsonTextWriter , следующий вызов 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);
        }
    }
}
  

Примечание Класс JsonTextReader в Jayrock является довольно либеральным анализатором текста JSON. На самом деле он позволяет гораздо больше синтаксиса, чем считается допустимым текстом JSON в соответствии с правилами, изложенными в RFC 4627. Например, класс 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, десериализует его в тип .NET с помощью XmlSerializer, а затем преобразует объект в текст JSON с помощью JsonConvert (эффективно преобразуя RSS в XML в текст 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 и связанных с ним типов можно найти в примерах, сопровождающих эту статью.

Использование JSON в ASP.NET

Изучив способы работы с JSON в JavaScript и из платформа .NET Framework с помощью Jayrock, пришло время обратиться к практическму примеру того, где и как можно применить все эти знания. Рассмотрим функцию обратного вызова скрипта клиента в ASP.NET 2.0, которая упрощает выполнение внеполосных вызовов из веб-браузера на страницу ASP.NET (или к определенному элементу управления на странице). В типичном сценарии обратного вызова клиентский скрипт в браузере упаковывает и отправляет данные обратно на веб-сервер для некоторой обработки методом на стороне сервера. Получив данные ответа от сервера, клиент использует их для обновления отображения браузера.

Примечание Дополнительные сведения см. в статье о обратных вызовах скриптов в журнале MSDN в 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 таким образом, чтобы при его изменении вызывается функция 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);
  

Метод GetCallBackEventReference в классе ClientScriptManager , который используется для создания кода JavaScript, который вызывает обратный вызов, имеет следующую сигнатуру:

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

Параметр аргумента указывает, какие данные отправляются с клиента на веб-сервер во время обратного вызова, а параметр clientCallback задает имя клиентской функции, вызываемой после завершения обратного вызова (showProducts). Вызов метода GetCallBackEventReference создает следующий код JavaScript и добавляет его в отображаемую разметку:

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

"[" + categoryID + "]" — это значение, которое передается серверу во время обратного вызова (массив с одним элементом categoryID), а showProducts — это функция JavaScript, которая выполняется при возврате обратного вызова.

На стороне сервера метод, выполняемый в ответ на обратный вызов, использует класс JsonConvert из Jayrock для анализа входящего текста 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 используется дважды: один раз для преобразования текста JSON в eventArgument в массив целых чисел, а затем для преобразования строкового массива productNames в текст JSON для возврата клиенту. Кроме того, мы могли бы использовать здесь классы JsonReader и JsonWriter , но JsonConvert выполняет ту же работу довольно хорошо, если используемые данные относительно малы и легко сопоставляются с существующими типами.

Когда данные возвращаются на стороне сервера, вызывается функция JavaScript, указанная из метода GetCallBackEventReference , и передается возвращаемое значение. Этот метод 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. Заворажность JSON заключается в его простоте для понимания, внедрения и реализации. Json практически не имеет кривой обучения для разработчиков, уже знакомых с JavaScript или другими языками программирования с аналогичной поддержкой полнофункциональных буквальных нот (например, Python и Ruby). Синтаксический анализ текста JSON в коде JavaScript можно выполнить путем простого вызова функции eval и создания текста JSON с помощью скрипта json.js, предоставленного по адресу http://www.json.org/json.js.

Существует множество библиотек для работы с JSON на всех основных платформах и платформах. В этой статье мы рассмотрели Jayrock, библиотеку с открытым кодом для создания и анализа текста JSON в приложениях .NET. Jayrock можно использовать в приложениях ASP.NET 1.x, 2.0 и Mono. ASP.NET AJAX предлагает аналогичные функции JSON, но только для приложений ASP.NET 2.0.

Счастливого программирования!

Ссылки

Ajax или AJAX?

Термин Ajax был первоначально придумано Джесси Джеймсом Гарреттом для описания стиля веб-приложений и набора технологий, участвующих в создании интерактивных веб-приложений. Исторически термин Ajax распространился в Интернете как акроним AJAX, что означает асинхронный JavaScript и XML. Со временем, однако, люди поняли, что "X" в AJAX не очень представительный базовый формат данных, используемый для взаимодействия с веб-сервером в фоновом режиме, так как большинство реализаций переходили на JSON в качестве более простой и эффективной альтернативы. Таким образом, вместо того, чтобы придумывать с заменой аббревиатуру, как AJAJ, что немного язык-скороговорка, аббревиатура, как правило, уходит в отставку в пользу Ajax термин, а не AJAX аббревиатуры.

На момент написания этой статьи, ожидайте увидеть смешанное и широкое использование "AJAX" и "Ajax", чтобы означать одно и то же. В этой статье мы застряли на "Ajax the term". Однако коммерческие продукты, предоставляющие платформы для приложений в стиле Ajax, как правило, используют форму аббревиатуры, чтобы отличаться от аналогичного продукта очистки и избежать любых потенциальных товарных знаков или юридических споров.

ASP.NET AJAX: внутри строки даты и времени JSON

Сериализатор JSON AJAX в ASP.NET кодирует экземпляр DateTime в виде строки JSON. Во время циклов предварительной версии ASP.NET AJAX использовал формат "@ticks@", где деления представляют количество миллисекундах с 1 января 1970 года в формате UTC. Дата и время в формате UTC, например 29 ноября 1989 г., 4:55:30, будут записаны как "@62831853071@". Несмотря на простоту и простоту, этот формат не может различать сериализованные значения даты и времени и строку, которая выглядит как сериализованная дата, но не предназначена для десериализации как единого. Следовательно, команда ASP.NET AJAX внесла изменения в окончательный выпуск, чтобы решить эту проблему, приняв формат "\/Date(ticks)\/".

Новый формат использует небольшой прием, чтобы снизить вероятность неправильного толкования. В JSON символ косой черты (/) в строке можно экранировать с помощью обратной косой черты (\), даже если это не обязательно. Используя это, команда ASP.NET AJAX изменила JavaScriptSerializer для записи экземпляра DateTime в виде строки "\/Date(ticks)\/". Экранирование двух косых черт является поверхностным, но имеет важное значение для JavaScriptSerializer. По правилам JSON \/Date(ticks)\/" технически эквивалентно "/Date(ticks)/,"" но JavaScriptSerializer десериализует первое значение как DateTime, а второе — как String. Таким образом, вероятность неоднозначности значительно ниже по сравнению с более простым форматом "@ticks@" из предварительных выпусков.

Отдельная благодарность

Перед отправкой этой статьи в MSDN у нас было несколько добровольцев, которые помогли прочитать статью и оставить отзыв о содержимом, грамматике и направлениях. Основными участниками процесса обзора являются Дуглас Крокфорд, Эрик Шенхольцер и Милан Негован.

Об авторах

Атиф Азиз (Atif Aziz) — главный консультант skybow AG, где его основное внимание уделяется тому, чтобы помочь клиентам понять и создать решения на платформе разработки .NET. Atif регулярно участвует в работе сообщества разработчиков Майкрософт, выступая на конференциях и создавая статьи для технических публикаций. Он является спикером INETA и президентом крупнейшей швейцарской группы пользователей .NET. К нему можно обращаться по адресу atif.aziz@skybow.com или через его веб-сайт по адресу http://www.raboof.com.

Скотт Митчелл (Scott Mitchell), автор шести книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с веб-технологиями Майкрософт с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его можно связать по адресу mitchell@4guysfromrolla.com или через его блог: http://ScottOnWriting.net.