PHP项目XML与数组如何相互转换

wen PHP项目 27

本文目录导读:

PHP项目XML与数组如何相互转换

  1. XML转数组
  2. 数组转XML
  3. 完整的生产级封装类
  4. 使用Composer包
  5. 注意事项

在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

注意事项

  1. CDATA处理:使用 LIBXML_NOCDATA 选项处理CDATA
  2. 中文字符:确保UTF-8编码
  3. 特殊字符:使用 htmlspecialchars() 转义XML特殊字符
  4. 性能考虑:大量数据时使用流式处理

这些方法覆盖了大部分使用场景,可以根据具体需求选择合适的方法。

抱歉,评论功能暂时关闭!