Преобразование XML в JSON на PHP (исходники)

Эдвард Принг, Джон Морар, Сентил Натан

Появление технологии Asynchronous JavaScript + XML (Ajax) вызвало новую волну энтузиазма в разработке Web-приложений, а также пересмотр многими разработчиками и проектировщиками своих методик создания Web-приложений. JavaScript Object Notation (JSON) представляет собой формат обмена данными, используемый для представления данных в бизнес-логику, выполняющуюся в браузерах. Многие Ajax-разработчики предпочитают обрабатывать данные напрямую, используя JSON в JavaScript-коде на стороне браузера. По мере расширения применения JSON в программах, работающих на серверах промежуточного уровня, станет необходимым предоставлять данные корпоративных приложений в браузеры в формате JSON, а не в XML-формате. Это означает, что разработчики должны преобразовать существующие корпоративные данные на стороне сервера, закодированные в XML-формате, в JSON-формат перед передачей их в браузер. В данной статье демонстрируется использование основанных на PHP серверных программ для преобразования данных приложения из XML-формата в JSON-формат.

Основы XML

XML - это стандарт для разметок. Основанная на XML разметка используется для описания данных, представляемых при помощи тегов, которые не обязательно должны быть определены заранее. XML очень хорошо расширяем, поскольку можно создавать новые теги по мере необходимости. В листинге 1 показан пример структуры данных, представленных в XML-формате.

Листинг 1. Простой пример XML-данных

                
<?xml version="1.0" encoding="UTF-8"?>
<contacts>
    <contact id="1">
        <name>John Doe</name>
        <phone>123-456-7890</phone>
        <address>
            <street>123 JFKStreet</street>
            <city>Any Town</city>
            <state>Any State</state>
            <zipCode>12345</zipCode>
        </address>
    </contact>
</contacts>

Первая строка - это XML-объявление, указывающее версию XML и используемую кодировку символов. Затем следует корневой элемент <contacts>, который охватывает несколько дочерних элементов. Вложенная структура дочерних элементов определяет данные для контакта. Обратите внимание на то, что элемент <address> содержит дочерние элементы, формирующие структуру поддерева ниже элемента <contact>. XML также позволяет указывать в открывающих тегах атрибуты, предоставляющие дополнительную информацию об элементах. Элемент <contact> имеет атрибут, присваивающий идентификатор этому элементу. XML-документ завершается закрывающим тегом </contacts> для корневого элемента.

Основы JSON

JSON не является языком разметки, как XML. Это текстовый формат обмена данными. Используется синтаксис JavaScript-объектов и массивов. В двух словах, JSON позволяет предоставлять данные просто. Например, объекты заключаются в фигурные скобки ({}), массивы в квадратные ([]). Фрагмент JavaScript-кода может без труда принять закодированные в JSON данные без выполнения какой-либо специального синтаксического анализа или дополнительного преобразования данных. В листинге 2 приведена структура данных, представленная в JSON-формате.

Листинг 2. Простой пример JSON-данных

                
{
   "contacts" : {
      "contact" : {
         "@attributes" : {
            "id" : "1"
         }, 
         "name" : "John Doe", 
         "phone" : "123-456-7890", 
         "address" : {
            "street" : "123 JFK Street", 
            "city" : "Any Town", 
            "state" : "Any State", 
            "zipCode" : "12345"
         }
      }
   }
}

Можно заметить, что каждый фрагмент данных, показанный в XML-формате в листинге 1, также присутствует в JSON-примере в листинге 2. Однако различие заключается в том, как JSON кодирует данные, используя типы данных JavaScript - объекты и массивы. Структура данных начинается с объекта, содержащего свойство с именем "contacts", которое само по себе является объектом. Этот объект имеет одно свойство с названием "contact", которое тоже является объектом. Этот объект содержит несколько свойств, формирующих детали контакта. Обратите внимание на то, что объект "contact" содержит другой (вложенный) объект "address", который описывает детали адреса. Как и в XML, данные в JSON-формате описывают сами себя, поэтому их может легко прочитать как человек, так и машина.

Обработка данных на стороне браузера

Теперь давайте рассмотрим, как данные обрабатываются в коде, выполняющемся на стороне браузера. Когда сервер передает XML-данные в браузер, эти XML-данные обрабатываются с использованием Document Object Model (DOM) API. Однако JavaScript-разработчики должны изучить все тонкости XML-обработки и написать много дополнительного кода для синтаксического разбора XML-данных, прежде чем с ними можно будет что-либо сделать. С появлением JSON код на стороне сервера может спакетировать и передать данные приложения в JSON-формате в ответ на запрос браузера. Если код на стороне сервера передает данные в JSON-формате в браузер, JavaScript-разработчикам не нужно писать какую-либо дополнительную сложную логику для синтаксического разбора XML. Кроме этого, полученные в JSON-формате данные будут готовы для обработки в коде на стороне браузера как родная для JavaScript структура данных. Фрагмент кода, приведенный в листинге 3, показывает, что для обработки данных в JSON-формате не нужна дополнительная работа.

Листинг 3. Фрагмент кода для обработки данных в JSON-формате, полученных от сервера

                
var result = httpRequest.responseText;
jsonResponse = eval('(' + result + ')');

Во фрагменте кода, выполняющегося на стороне браузера и приведенного в листинге 3, result - это строка в JSON-формате, полученная от сервера. Все что требуется - одна строка кода для преобразования этих строковых данных в родной для JavaScript тип данных путем использования выражения eval. Можно заметить, что работать с JSON-данными на стороне браузера намного проще. Использование JSON привносит простоту и смысл в код на стороне браузера.

Выражение eval в листинге 3 будет выполняться независимо от того, что возвратит сервер, поэтому здесь имеет место риск нарушения системы защиты. Этот риск ограничен, поскольку политика системы защиты браузера ограничивает HTTP-запросы в JavaScript только тем сервером, с которого код был первоначально загружен. Но все-таки в некоторых ситуациях было бы благоразумно проверить получаемые от сервера данные на соответствие ожидаемым (возможно, с использованием регулярных выражений) перед передачей их в выражение eval.

Преобразование XML-в-JSON

Все большее количество приложений нуждается в преобразовании XML-данных в JSON. Несколько Web-сервисов, выполняющих такие преобразования, уже появились. IBM T.J. Watson Research Center разработал особый метод, использующий PHP для выполнения преобразования. Этот метод принимает XML-данные на входе и преобразует их в JSON-формат на выходе. Такое PHP-решение обеспечивает несколько преимуществ:

  • Его можно запускать в автономном режиме, из командной строки.
  • Его можно включить в существующий код, выполняющийся на стороне сервера.
  • Его можно легко разместить как Web-сервис в Web.

Преобразование XML-в-JSON требует использования двух базовых функциональных возможностей PHP:

  • SimpleXMLElement
  • Services_JSON с http://pear.php.net

SimpleXMLElement поддерживается в PHP версии 5 и выше. Это объект со свойствами, содержащими данные, хранимые в XML-документе. Services_JSON - это предложение с открытым исходным кодом (появившееся в конце 2005), реализуемое на PHP. Оно предоставляет функции JSON-кодирования (PHP-данные в JSON) и декодирования (JSON в PHP-данные). Эта библиотека PHP-классов с открытым исходным кодом в настоящее время доступна на Web-сайте PEAR.

Используя только эти две базовые функциональные возможности PHP, можно преобразовать любые произвольные XML-данные в JSON-формат. Прежде всего, нужно преобразовать XML-содержимое в подходящий PHP-тип данных, используя SimpleXMLElement. Затем PHP-данные предоставляются в кодировщик Services_JSON, который, в свою очередь, формирует окончательный вывод данных в JSON-формате.

Освоение PHP-кода

Данная реализация xml2json состоит из трех частей:

  • xml2json.php - PHP-класс с двумя статическими функциями.
  • xml2json_test.php - Тестовый драйвер для работы с функцией преобразования xml2json.
  • test1.xml, test2.xml, test3.xml, test4.xml - XML-файлы различной сложности.

Для простоты в данной статье не приводятся подробные комментарии в исходном коде. Однако исходный код в поставляемых со статьей файлах содержит полные комментарии. Обращайтесь к этим исходным файлам за подробной информацией о логике работы программы.

В листинге 4 определяются некоторые полезные константы. Первая строка кода импортирует реализацию Services_JSON.

Листинг 4. Определение констант в xml2json.php

                
require_once 'json/JSON.php';
// Внутренний параметр Debug, специфичный для программы.
define ("DEBUG", false);
// Максимальная глубина рекурсии, которую мы можем позволить.
define ("MAX_RECURSION_DEPTH_ALLOWED", 25);
// Пустая строка.
define ("EMPTY_STR", "");
// Имя свойства объекта SimpleXMLElement для атрибутов. 
define ("SIMPLE_XML_ELEMENT_OBJECT_PROPERTY_FOR_ATTRIBUTES", "@attributes");
// Имя объекта SimpleXMLElement.
define ("SIMPLE_XML_ELEMENT_PHP_CLASS", "SimpleXMLElement");

Фрагмент кода, приведенный в листинге 5, представляет собой входную функцию в преобразователь xml2json. Она принимает в качестве входного параметра XML-данные и преобразует XML-строку в объект SimpleXMLElement, который передается в другую функцию (рекурсивно) в данном классе. Эта функция преобразует XML-элементы в ассоциативный PHP-массив. Данный массив затем передается в качестве входного параметра в кодировщик Services_JSON, который выдает данные в JSON-формате.

Листинг 5. Использование Services_JSON в xml2json.php

                
public static function transformXmlStringToJson($xmlStringContents) {
    $simpleXmlElementObject = simplexml_load_string($xmlStringContents); 

if ($simpleXmlElementObject == null) { return(EMPTY_STR); }
$jsonOutput = EMPTY_STR;
// Преобразовать XML-структуру в PHP-массив. $array1 = xml2json::convertSimpleXmlElementObjectIntoArray($simpleXmlElementObject);
if (($array1 != null) && (sizeof($array1) > 0)) { // Создать новый экземпляр Services_JSON $json = new Services_JSON(); // Преобразовать его в данные в формате JSON. $jsonOutput = $json->encode($array1); } // Конец if (($array1 != null) && (sizeof($array1) > 0))
return($jsonOutput); } // Конец функции transformXmlStringToJson

Длинный фрагмент кода, приведенный в листинге 6, использует методологию рекурсии, разработанную PHP-сообществом сторонников открытого кода. Он принимает объект SimpleXMLElement в качестве входного параметра и рекурсивно проходит по вложенному XML-дереву. Он сохраняет все обнаруженные XML-элементы в ассоциативном PHP-массиве. Вы можете настроить предельную глубину рекурсии, изменив константу, определенную в листинге 4.

Листинг 6. Логика преобразования в xml2json.php

                
public static function convertSimpleXmlElementObjectIntoArray($simpleXmlElementObject,
&$recursionDepth=0) { 
  // Следить за глубиной рекурсии.

if ($recursionDepth > MAX_RECURSION_DEPTH_ALLOWED) { // Фатальная ошибка. Выйти сейчас. return(null); }
if ($recursionDepth == 0) { if (get_class($simpleXmlElementObject) != SIMPLE_XML_ELEMENT_PHP_CLASS) { // Если вызывающая функция не вызывала эту функцию изначально // с объектом SimpleXMLElement, возвратить управление. return(null); } else { // Сохранить оригинальный SimpleXmlElementObject, переданный вызывающей функцией. // Он нам понадобится в самом конце, когда будем возвращаться отсюда навсегда. $callerProvidedSimpleXmlElementObject = $simpleXmlElementObject; } } // Конец if ($recursionDepth == 0) {
if (get_class($simpleXmlElementObject) == SIMPLE_XML_ELEMENT_PHP_CLASS) { // Получить копию simpleXmlElementObject $copyOfsimpleXmlElementObject = $simpleXmlElementObject; // Получить объектные переменные в объект SimpleXmlElement для итерации. $simpleXmlElementObject = get_object_vars($simpleXmlElementObject); }
// Должен быть массивом объектных переменных. if (is_array($simpleXmlElementObject)) { // Инициализировать получаемый массив. $resultArray = array(); // Равен ли размер входного массива 0? Мы достигли нечастого текста CDATA, если есть. if (count($simpleXmlElementObject) <= 0) { // Вернуть одиночный текст CDATA. Это мог бы быть даже // пустой элемент, или просто заполненный пробелами. return (trim(strval($copyOfsimpleXmlElementObject))); }
// Пройдем по дочерним элементам. foreach($simpleXmlElementObject as $key=>$value) { // Если этот блок кода закомментирован, XML-атрибуты будут // добавлены в массив result. // Удалите комментарии из следующего блока кода, если XML-атрибуты НЕ // нужно возвращать как часть массива result. /* if((is_string($key)) && ($key == SIMPLE_XML_ELEMENT_OBJECT_PROPERTY_FOR_ATTRIBUTES)) { continue; } */
// Обработать рекурсивно только что обнаруженный текущий элемент. // Увеличить глубину рекурсии на единицу. $recursionDepth++; $resultArray[$key] = xml2json::convertSimpleXmlElementObjectIntoArray($value, $recursionDepth);
// Уменьшить глубину рекурсии на единицу. $recursionDepth--; } // Конец foreach($simpleXmlElementObject as $key=>$value) {
if ($recursionDepth == 0) { // Это все. Начинаем выходить. // Установить имя корневого XML-элемента как корневой [top-level] ключ // ассоциативного массива, который мы собираемся возвратить функции, // вызвавшей рекурсивную функцию. $tempArray = $resultArray; $resultArray = array(); $resultArray[$callerProvidedSimpleXmlElementObject->getName()] = $tempArray; }
return ($resultArray); } else { // Теперь ищем либо текст XML-атрибута, либо // текст между XML-тегами. return (trim(strval($simpleXmlElementObject))); } // Конец else } // Конец функции convertSimpleXmlElementObjectIntoArray.

В конце успешного прохождения по XML-дереву эта функция преобразует и сохранит все XML-элементы (корневой элемент и все дочерние элементы) в ассоциативном PHP-массиве. Для сложных XML-документов получаемый PHP-массив будет таким же сложным. После завершения формирования этого PHP-массива кодировщик Services_JSON может легко преобразовать его в данные JSON-формата. Чтобы понять логику рекурсии, просмотрите документированный исходный файл.

Реализация тестового драйвера для xml2json

Фрагмент кода, приведенный в листинге 7, представляет собой тестовый драйвер, применяющий логику преобразования xml2json.

Листинг 7. xml2json_test.php

                
<?php
    require_once("xml2json.php");

// Имя файла, из которого читается XML-содержимое. $testXmlFile = "";
// Прочитать имя файла из командной строки. if ($argc <= 1) { print("Please provide the XML filename as a command-line argument:\n"); print("\tphp -f xml2json_test.php test1.xml\n"); return; } else { $testXmlFile = $argv[1]; }
//Прочитать XML-содержимое из входного файла. file_exists($testXmlFile) or die('Could not find file ' . $testXmlFile); $xmlStringContents = file_get_contents($testXmlFile);
$jsonContents = ""; // Преобразовать в формат JSON. // xml2json просто принимает объект String, содержащий XML-содержимое, // в качестве входного параметра. $jsonContents = xml2json::transformXmlStringToJson($xmlStringContents);
echo("JSON formatted output generated by xml2json:\n\n"); echo($jsonContents); ?>

Вы можете выполнить программу из командной строки с именем XML-файла, указанным как аргумент командной строки:

php -f xml2json_test.php test2.xml

При выполнении из командной строки программа читает XML-содержимое из файла в строковую переменную. Затем она вызывает статическую функцию в классе xml2json для получения результата в JSON-формате. Кроме выполнения программы из командной строки вы можете изменить логику в этом исходном файле для предоставления преобразователя xml2json в виде вызываемого Web-сервиса, использующего протоколы доступа Simple Object Access Protocol (SOAP) или Representational State Transfer (REST). При необходимости можно легко сделать это в PHP с минимальными затратами.

В листинге 8 приведен один из четырех тестовых XML-файлов, поставляемых со статьей для тестирования реализации xml2json. Степень сложности этих файлов различна. Вы можете передать один из этих файлов в виде аргумента командной строки в тестовый драйвер xml2json_test.php.

Листинг 8. Тестирование реализации xml2json с test2.xml

                
<?xml version="1.0" encoding="UTF-8"?>
<books>
    <book id="1">
        <title>Code Generation in Action</title>
        <author><first>Jack</first><last>Herrington</last></author>
        <publisher>Manning</publisher>
    </book>

<book id="2"> <title>PHP Hacks</title> <author><first>Jack</first><last>Herrington</last></author> <publisher>O'Reilly</publisher> </book>
<book id="3"> <title>Podcasting Hacks</title> <author><first>Jack</first><last>Herrington</last></author> <publisher>O'Reilly</publisher> </book> </books>

Фрагмент кода, приведенный в листинге 9, представляет собой результат в формате JSON при использовании файла test2.xml в качестве аргумента командной строки для тестового драйвера xml2json_test.php.

Листинг 9. Результат в JSON-формате для test2.xml

                
{
   "books" : {
      "book" : [ {
         "@attributes" : {
            "id" : "1"
         }, 
         "title" : "Code Generation in Action", 
         "author" : {
            "first" : "Jack", "last" : "Herrington"
         }, 
         "publisher" : "Manning"
      }, {
         "@attributes" : {
            "id" : "2"
         }, 
         "title" : "PHP Hacks", "author" : {
            "first" : "Jack", "last" : "Herrington"
         }, 
         "publisher" : "O'Reilly"
      }, {
         "@attributes" : {
            "id" : "3"
         }, 
         "title" : "Podcasting Hacks", "author" : {
            "first" : "Jack", "last" : "Herrington"
         }, 
         "publisher" : "O'Reilly"
      }
   ]}
}

Обратите внимание на то, что XML-атрибут id для элемента <book> сохраняется в JSON-данных как свойство объекта "@attributes", а элемент <book> сохраняется как массив объектов. Полученные данные в JSON-формате готовы для приема в JavaScript-коде с использованием выражения eval.

Заключение

JSON только начинает приобретать вес среди Web-разработчиков. Его преимущества, видимые главным образом JavaScript-разработчиками, состоят в элегантности и простоте. В определенных ситуациях JSON может быть достойной альтернативой XML. В данной статье аргументируется необходимость преобразования XML-в-JSON на сервере промежуточного уровня. Затем внимание уделяется логическому обоснованию использования корпоративных данных в XML-формате в виде данных в формате JSON, для того чтобы программы на стороне браузера могли легко использовать их. Предоставляется PHP-код, который может выполнять преобразование XML-в-JSON.

Вы можете использовать исходный код, поставляемый с данной статьей, в различных целях - как автономную программу, как библиотеку классов для существующей программы, выполняющейся на стороне сервера, либо как функцию Web-сервиса SOAP/REST для участия в корпоративной сервис-ориентированной архитектуре (Service-Oriented Architecture - SOA).


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=8932