异步JavaScript + XML(Ajax)的出现使Web应用程序开发产生了新的热情,并使许多架构师和开发人员重新考虑他们创建Web应用程序的方式。 JavaScript对象表示法(JSON)是一种数据交换格式,用于表示在浏览器上运行的业务逻辑中的数据。 许多Ajax开发人员更喜欢在浏览器端JavaScript代码中直接使用JSON处理数据。 随着JSON的使用增加,中间件服务器程序将有必要以JSON格式而不是XML格式向浏览器提供企业应用程序数据。 这意味着开发人员需要将以XML编码的现有服务器端企业数据转换为JSON,然后再将其发送到浏览器。 本文向您展示了如何在将基于XML的应用程序数据发送到浏览器应用程序之前,使用基于PHP的服务器程序将XML格式的应用程序数据转换为JSON格式。
XML是定义标记的标准。 基于XML的标记用于描述通过不需要预定义的标签表示的数据。 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>
元素具有为该元素分配id
属性的属性。 XML文档的结尾元素是</contacts>
,它是根元素的结束标记。
JSON不是像XML这样的标记语言。 相反,它是基于文本的数据交换格式。 它是JavaScript对象和数组的数据语法。 简而言之,JSON使您可以简单地表示数据。 例如,将对象括在大括号({})中,并将数组括在方括号([])中。 一段JavaScript代码可以很容易地使用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"
}
}
}
}
您可以看到清单1的XML示例中显示的每条数据也在清单2的JSON示例中也存在。 但是,不同之处在于JSON如何使用JavaScript对象和数组数据类型对数据进行编码。 数据结构从一个对象开始,该对象包括一个名为"contacts"
的属性,该属性本身就是一个对象。 该对象具有一个名为"contact"
属性,它也是一个对象。 该对象包括几个属性,这些属性构成了联系人的详细信息。 注意, "contact"
对象包括另一个名为"address"
(嵌套)对象,该对象描述了地址的详细信息。 与XML一样,JSON格式的数据是自描述的,因此人和机器都可以轻松读取它。
现在让我们看一下如何在浏览器端代码中处理数据。 当服务器将XML数据发送到浏览器时,将使用文档对象模型(DOM)API处理该XML数据。 但是,JavaScript开发人员必须学习XML处理的所有复杂性,并编写大量其他代码来解析XML数据,然后才能对该数据进行任何处理。 但是,随着JSON的出现,服务器端代码可以打包和发送JSON编码的应用程序数据,以响应浏览器的请求。 如果服务器端代码将JSON格式的数据发送到浏览器,则JavaScript开发人员无需编写任何其他复杂的逻辑即可解析XML。 另外,以JSON格式接收的数据将很容易在浏览器端代码中视为JavaScript本机数据结构。 清单3中的代码片段显示,处理JSON格式的数据不需要任何额外的工作。
var result = httpRequest.responseText;
jsonResponse = eval('(' + result + ')');
在清单3的浏览器端代码片段中, result
是从服务器接收到的JSON格式的字符串。 通过使用eval
语句评估JSON格式的字符串,只需一行代码即可将字符串数据转换为本地JavaScript数据类型。 您可以看到,在浏览器端处理JSON数据要容易得多。 JSON的使用为浏览器端代码带来了简单性和价值。
清单3中的eval
语句将执行服务器返回的所有代码,因此这里存在安全风险。 风险是有限的,因为浏览器安全策略将JavaScript中的HTTP请求限制为原始加载代码的服务器。 但是,在某些情况下,在将其传递到eval
语句之前,可能要谨慎地验证从服务器接收到的数据是否符合期望(也许使用正则表达式)。
越来越多的应用程序需要将XML数据转换为JSON。 已经出现了几种基于Web的服务来进行此类转换。 IBM TJ Watson研究中心开发了一种特殊的方法,该方法使用PHP进行转换。 这种方法接受XML字符串数据作为输入,并将其转换为JSON格式的数据输出。 这种基于PHP的解决方案具有以下优点:
XML到JSON的转换需要几个PHP核心功能:
PHP 5及更高版本支持SimpleXMLElement
。 它是一个对象,其属性包含XML文档中保存的数据。 Services_JSON
是PHP开源提案,于2005年底获得批准。它提供JSON特定的编码器(PHP数据到JSON)和解码器(JSON到PHP数据)功能。 现在可以通过PEAR网站获得此开放源代码PHP类库(请参阅参考资料 )。
通过仅使用PHP的这两个核心功能,可以将任意XML数据转换为JSON。 首先,您需要使用SimpleXMLElement
将XML内容转换为合适PHP数据类型。 然后,将PHP数据提供给Services_JSON
编码器,后者再产生最终的JSON格式的输出。
此xml2json
实现包含三个部分:
xml2json
转换功能的测试驱动程序 为简单起见,本文未在代码中显示详细注释。 但是,附加的源文件中的代码确实包含完整的注释。 有关程序逻辑的完整详细信息,请参阅随附的源文件(请参阅下载 )。
清单4定义了一些有用的常量。 代码的第一行导入Services_JSON
实现。
require_once 'json/JSON.php';
// Internal program-specific Debug option.
define ("DEBUG", false);
// Maximum Recursion Depth that we can allow.
define ("MAX_RECURSION_DEPTH_ALLOWED", 25);
// An empty string
define ("EMPTY_STR", "");
// SimpleXMLElement object property name for attributes
define ("SIMPLE_XML_ELEMENT_OBJECT_PROPERTY_FOR_ATTRIBUTES", "@attributes");
// SimpleXMLElement object name.
define ("SIMPLE_XML_ELEMENT_PHP_CLASS", "SimpleXMLElement");
清单5中显示的代码段是xml2json
转换器的入口函数。 它以XML数据作为输入,并将XML字符串转换为SimpleXMLElement
对象,该对象作为输入发送到此类中的另一个(递归)函数。 然后,此函数将XML元素转换为PHP关联数组。 然后将该数组作为输入传递到Services_JSON
编码器,该编码器为您提供JSON格式的输出。
public static function transformXmlStringToJson($xmlStringContents) {
$simpleXmlElementObject = simplexml_load_string($xmlStringContents);
if ($simpleXmlElementObject == null) {
return(EMPTY_STR);
}
$jsonOutput = EMPTY_STR;
// Let us convert the XML structure into PHP array structure.
$array1 = xml2json::convertSimpleXmlElementObjectIntoArray($simpleXmlElementObject);
if (($array1 != null) && (sizeof($array1) > 0)) {
// Create a new instance of Services_JSON
$json = new Services_JSON();
// Let us now convert it to JSON formatted data.
$jsonOutput = $json->encode($array1);
} // End of if (($array1 != null) && (sizeof($array1) > 0))
return($jsonOutput);
} // End of function transformXmlStringToJson
清单6中显示的冗长的代码段使用了一种受PHP开源社区启发的递归技术(请参阅参考资料 )。 它接受SimpleXMLElement
对象作为输入,并在嵌套的XML树中进行递归树遍历。 它将所有访问的XML元素存储在PHP关联数组中。 您可以通过更改清单4中定义的常量来调整递归深度限制。
public static function convertSimpleXmlElementObjectIntoArray($simpleXmlElementObject,
&$recursionDepth=0) {
// Keep an eye on how deeply we are involved in recursion.
if ($recursionDepth > MAX_RECURSION_DEPTH_ALLOWED) {
// Fatal error. Exit now.
return(null);
}
if ($recursionDepth == 0) {
if (get_class($simpleXmlElementObject) != SIMPLE_XML_ELEMENT_PHP_CLASS) {
// If the external caller doesn't call this function initially
// with a SimpleXMLElement object, return now.
return(null);
} else {
// Store the original SimpleXmlElementObject sent by the caller.
// We will need it at the very end when we return from here for good.
$callerProvidedSimpleXmlElementObject = $simpleXmlElementObject;
}
} // End of if ($recursionDepth == 0) {
if (get_class($simpleXmlElementObject) == SIMPLE_XML_ELEMENT_PHP_CLASS) {
// Get a copy of the simpleXmlElementObject
$copyOfsimpleXmlElementObject = $simpleXmlElementObject;
// Get the object variables in the SimpleXmlElement object for us to iterate.
$simpleXmlElementObject = get_object_vars($simpleXmlElementObject);
}
// It needs to be an array of object variables.
if (is_array($simpleXmlElementObject)) {
// Initialize the result array.
$resultArray = array();
// Is the input array size 0? Then, we reached the rare CDATA text if any.
if (count($simpleXmlElementObject) <= 0) {
// Let us return the lonely CDATA. It could even be
// an empty element or just filled with whitespaces.
return (trim(strval($copyOfsimpleXmlElementObject)));
}
// Let us walk through the child elements now.
foreach($simpleXmlElementObject as $key=>$value) {
// When this block of code is commented, XML attributes will be
// added to the result array.
// Uncomment the following block of code if XML attributes are
// NOT required to be returned as part of the result array.
/*
if((is_string($key)) && ($key == SIMPLE_XML_ELEMENT_OBJECT_PROPERTY_FOR_ATTRIBUTES)) {
continue;
}
*/
// Let us recursively process the current element we just visited.
// Increase the recursion depth by one.
$recursionDepth++;
$resultArray[$key] =
xml2json::convertSimpleXmlElementObjectIntoArray($value, $recursionDepth);
// Decrease the recursion depth by one.
$recursionDepth--;
} // End of foreach($simpleXmlElementObject as $key=>$value) {
if ($recursionDepth == 0) {
// That is it. We are heading to the exit now.
// Set the XML root element name as the root [top-level] key of
// the associative array that we are going to return to the caller of this
// recursive function.
$tempArray = $resultArray;
$resultArray = array();
$resultArray[$callerProvidedSimpleXmlElementObject->getName()] = $tempArray;
}
return ($resultArray);
} else {
// We are now looking at either the XML attribute text or
// the text between the XML tags.
return (trim(strval($simpleXmlElementObject)));
} // End of else
} // End of function convertSimpleXmlElementObjectIntoArray.
在成功完成XML树遍历后,此函数将转换所有XML元素(根元素和所有子元素)并将其存储在PHP关联数组中。 对于复杂的XML文档,生成PHP数组将同样复杂。 一旦完全构建了PHP数组,然后Services_JSON
编码器就可以轻松将其转换为JSON格式的数据。 要了解此递归逻辑,请签出已记录的源文件。
清单7中显示的代码片段是一个测试驱动程序,它练习xml2json
转换器逻辑。
<?php
require_once("xml2json.php");
// Filename from where XML contents are to be read.
$testXmlFile = "";
// Read the filename from the command line.
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];
}
//Read the XML contents from the input file.
file_exists($testXmlFile) or die('Could not find file ' . $testXmlFile);
$xmlStringContents = file_get_contents($testXmlFile);
$jsonContents = "";
// Convert it to JSON now.
// xml2json simply takes a String containing XML contents as input.
$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格式的结果。 除了从命令行运行程序之外,还可以修改此源文件中的逻辑,以使用简单对象访问协议(SOAP)或表示状态传输(REST)访问协议将xml2json
转换器公开为可远程调用的Web服务。 如果需要,您可以用最少的工作在PHP中轻松执行该操作。
清单8显示了本文提供的用于测试xml2json
实现的四个测试XML文件xml2json
。 复杂程度因一个测试文件而异。 您可以将这些文件之一作为命令行参数传递给测试驱动程序xml2json_test.php。
<?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>
当您使用test2.xml作为测试驱动程序xml2json_test.php的命令行参数时, 清单9中显示的代码段是JSON格式的结果。
{
"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"
}
]}
}
请注意, <book>
元素的XML属性id
作为"@attributes"
对象属性存储在JSON数据中,而<book>
元素作为对象数组存储在JSON数据中。 可以使用eval
语句在JavaScript代码中轻松使用此JSON输出。
JSON在Web开发人员中才刚刚开始兴起。 它的成功(主要在JavaScript开发人员中看到)是由于其优雅和简单。 在某些情况下,JSON可以替代XML。 本文总结了在中间件服务器层进行XML到JSON转换的需求。 它进一步强调了将现有的XML编码的企业数据作为JSON格式的数据加以利用的基本原理,以便浏览器端程序可以轻松使用它。 它提供了可以执行XML到JSON转换PHP代码。 (请参阅下载 。)
您可以通过多种方式使用本文中提供的源代码-作为独立工具,作为要在现有服务器端程序中使用的共享类库,或者作为参与企业的SOAP / REST Web服务功能面向服务的体系结构(SOA)。
翻译自: https://www.ibm.com/developerworks/opensource/library/x-xml2jsonphp/index.html