本文目录导读:

在PHP项目中,XML与数组的相互转换是非常常见的需求,以下是几种常用的实现方法:
XML转数组
使用 simplexml_load_string() 和 json_* 函数
function xmlToArray($xmlString) {
// 加载XML字符串
$xml = simplexml_load_string($xmlString, 'SimpleXMLElement', LIBXML_NOCDATA);
// 转换为JSON再转数组
$json = json_encode($xml);
$array = json_decode($json, true);
return $array;
}
// 使用示例
$xmlString = '<?xml version="1.0" encoding="UTF-8"?>
<root>
<name>张三</name>
<age>25</age>
<city>北京</city>
<hobbies>
<hobby>读书</hobby>
<hobby>运动</hobby>
</hobbies>
</root>';
$result = xmlToArray($xmlString);
print_r($result);
使用递归函数
function xmlToArrayRecursive($xmlObject, $out = []) {
foreach ((array)$xmlObject as $index => $node) {
$out[$index] = (is_object($node)) ?
xmlToArrayRecursive($node) : $node;
}
return $out;
}
// 使用示例
$xml = simplexml_load_string($xmlString, 'SimpleXMLElement', LIBXML_NOCDATA);
$array = xmlToArrayRecursive($xml);
数组转XML
使用 SimpleXMLElement
function arrayToXml($array, $rootElement = 'root', $xml = null) {
if ($xml === null) {
$xml = new SimpleXMLElement("<?xml version=\"1.0\" encoding=\"UTF-8\"?><{$rootElement}></{$rootElement}>");
}
foreach ($array as $key => $value) {
// 处理数字键名
if (is_numeric($key)) {
$key = 'item';
}
if (is_array($value)) {
// 检查是否为关联数组
$isAssoc = array_keys($value) !== range(0, count($value) - 1);
if ($isAssoc) {
$child = $xml->addChild($key);
arrayToXml($value, null, $child);
} else {
// 索引数组
foreach ($value as $item) {
$child = $xml->addChild($key);
if (is_array($item)) {
arrayToXml($item, null, $child);
} else {
$child->{0} = htmlspecialchars($item);
}
}
}
} else {
$xml->addChild($key, htmlspecialchars($value));
}
}
return $xml->asXML();
}
// 使用示例
$data = [
'name' => '张三',
'age' => 25,
'city' => '北京',
'hobbies' => ['读书', '运动', '编程'],
'address' => [
'province' => '北京',
'city' => '朝阳区',
'detail' => '某某路100号'
]
];
$xmlString = arrayToXml($data);
echo $xmlString;
使用 DOMDocument
function arrayToXmlDOM($array, $rootName = 'root') {
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;
$root = $dom->createElement($rootName);
$dom->appendChild($root);
createXmlFromArray($array, $root, $dom);
return $dom->saveXML();
}
function createXmlFromArray($array, $parentElement, $dom) {
foreach ($array as $key => $value) {
// 处理数字键名
if (is_numeric($key)) {
$key = 'item';
}
$element = $dom->createElement($key);
if (is_array($value)) {
createXmlFromArray($value, $element, $dom);
} else {
$element->appendChild($dom->createTextNode($value));
}
$parentElement->appendChild($element);
}
}
// 使用示例
$xmlString = arrayToXmlDOM($data);
echo $xmlString;
完整的生产级封装类
class XmlArrayConverter
{
/**
* XML转数组
*/
public static function toArray($xmlString, $options = LIBXML_NOCDATA)
{
if (empty($xmlString)) {
return [];
}
// 如果是SimpleXMLElement对象
if ($xmlString instanceof SimpleXMLElement) {
$xml = $xmlString;
} else {
// 允许字符串缺失XML声明
if (strpos($xmlString, '<?xml') === false) {
$xmlString = '<?xml version="1.0" encoding="UTF-8"?>' . $xmlString;
}
libxml_use_internal_errors(true);
$xml = simplexml_load_string($xmlString, 'SimpleXMLElement', $options);
if ($xml === false) {
$errors = libxml_get_errors();
libxml_clear_errors();
throw new \InvalidArgumentException('XML解析错误: ' . print_r($errors, true));
}
}
return self::simpleXmlToArray($xml);
}
/**
* 递归转换SimpleXMLElement为数组
*/
private static function simpleXmlToArray($xml)
{
$result = [];
foreach ($xml->children() as $key => $child) {
$count = $xml->{$key}->count();
$attributes = $child->attributes();
$value = (string)$child;
// 如果有子元素
if ($child->children()->count() > 0) {
$value = self::simpleXmlToArray($child);
}
// 保留属性
if (count($attributes) > 0) {
$value = [
'@attributes' => iterator_to_array($attributes),
'@value' => $value
];
}
// 处理重复元素
if ($count > 1) {
if (!isset($result[$key])) {
$result[$key] = [];
}
$result[$key][] = $value;
} else {
$result[$key] = $value;
}
}
return $result;
}
/**
* 数组转XML
*/
public static function toXml($array, $rootElement = 'root', $encoding = 'UTF-8')
{
if (empty($array)) {
return '';
}
$xml = new SimpleXMLElement("<?xml version=\"1.0\" encoding=\"{$encoding}\"?><{$rootElement}></{$rootElement}>");
self::arrayToSimpleXml($array, $xml);
return $xml->asXML();
}
/**
* 递归转换数组为SimpleXMLElement
*/
private static function arrayToSimpleXml($array, &$xml)
{
foreach ($array as $key => $value) {
// 处理数字键名
if (is_numeric($key)) {
$key = 'item_' . $key;
}
if (is_array($value)) {
// 检查是否为索引数组(多个相同元素)
$isIndexed = array_keys($value) === range(0, count($value) - 1);
if ($isIndexed) {
foreach ($value as $item) {
$child = $xml->addChild($key);
if (is_array($item)) {
self::arrayToSimpleXml($item, $child);
} else {
$child[0] = self::escapeValue($item);
}
}
} else {
// 关联数组
if (isset($value['@attributes'])) {
$child = $xml->addChild($key);
foreach ($value['@attributes'] as $attrKey => $attrValue) {
$child->addAttribute($attrKey, $attrValue);
}
if (isset($value['@value'])) {
$child[0] = self::escapeValue($value['@value']);
} else {
self::arrayToSimpleXml($value, $child);
}
} else {
$child = $xml->addChild($key);
self::arrayToSimpleXml($value, $child);
}
}
} else {
$xml->addChild($key, self::escapeValue($value));
}
}
}
/**
* 转义特殊字符
*/
private static function escapeValue($value)
{
if ($value === null) {
return '';
}
return htmlspecialchars((string)$value, ENT_XML1 | ENT_QUOTES, 'UTF-8');
}
}
// 使用示例
$converter = new XmlArrayConverter();
// XML转数组
$xml = '<root><name>张三</name><age>25</age></root>';
$array = $converter->toArray($xml);
// 数组转XML
$data = ['name' => '李四', 'age' => 30];
$xml = $converter->toXml($data, 'person');
使用Composer包
推荐使用成熟的Composer包:
composer require ezyang/htmlpurifier # 或 composer require seldaek/jsonlint
但更推荐专门的XML处理库:
composer require mtazumal/xml-to-array
注意事项
- CDATA处理:使用
LIBXML_NOCDATA选项处理CDATA - 中文字符:确保UTF-8编码
- 特殊字符:使用
htmlspecialchars()转义XML特殊字符 - 性能考虑:大量数据时使用流式处理
这些方法覆盖了大部分使用场景,可以根据具体需求选择合适的方法。