PHP解析具有许多名称空间的XML响应


PHP Parse XML response with many namespaces

有没有一种方法可以在PHP中解析XML响应,考虑所有名称空间的节点,并在不知道所有节点名称的情况下将其转换为对象或数组?

例如,转换此:

<?xml version="1.0" encoding="ISO-8859-1"?>
<serv:message xmlns:serv="http://www.webex.com/schemas/2002/06/service"
    xmlns:com="http://www.webex.com/schemas/2002/06/common"
    xmlns:att="http://www.webex.com/schemas/2002/06/service/attendee">
    <serv:header>
        <serv:response>
            <serv:result>SUCCESS</serv:result>
            <serv:gsbStatus>PRIMARY</serv:gsbStatus>
        </serv:response>
    </serv:header>
    <serv:body>
        <serv:bodyContent xsi:type="att:lstMeetingAttendeeResponse"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <att:attendee>
                <att:person>
                    <com:name>James Kirk</com:name>
                    <com:firstName>James</com:firstName>
                    <com:lastName>Kirk</com:lastName>
                    <com:address>
                        <com:addressType>PERSONAL</com:addressType>
                    </com:address>
                    <com:phones />
                    <com:email>Jkirk@sz.webex.com</com:email>
                    <com:type>VISITOR</com:type>
                </att:person>
                <att:contactID>28410622</att:contactID>
                <att:joinStatus>INVITE</att:joinStatus>
                <att:meetingKey>803754412</att:meetingKey>
            </att:attendee>
        </serv:bodyContent>
    </serv:body>
</serv:message>

类似于:

['message' => [
    'header' => [
        'response' => [
            'result' => 'SUCCESS',
            'gsbStatus' => 'PRIMARY'
        ]
    ],
    'body' => [
        'bodyContent' => [
            'attendee' => [
                'person' => [
                    'name' => 'James Kirk',
                    'firstName' => 'James',
                    ...
                ],
                'contactID' => 28410622,
                ...
            ]
        ]
    ]
]

我知道使用无名称空间的节点很容易,但我不知道从哪里开始处理这样的事情。

(阅读@ThW关于为什么一个数组实际上并不那么重要的答案)

我知道使用无名称空间的节点很容易,但我不知道从哪里开始处理这样的事情。

这和使用名称空间节点一样简单,因为从技术上讲,这些节点是相同的。让我们举一个快速的例子,以下脚本在文档中的所有元素上循环,而不考虑名称空间:

$result = $xml->xpath('//*');
foreach ($result as $element) {
    $depth = count($element->xpath('./ancestor::*'));
    $indent = str_repeat('  ', $depth);
    printf("%s %s'n", $indent, $element->getName());
}

在您的案例中,输出为:

 message
   header
     response
       result
       gsbStatus
   body
     bodyContent
       attendee
         person
           name
           firstName
           lastName
           address
             addressType
           phones
           email
           type
         contactID
         joinStatus
         meetingKey

正如您所看到的,您可以迭代所有元素,就好像它们根本没有任何命名空间一样。

但正如前面所概述的,当您忽略名称空间时,您也会丢失重要信息。例如,对于您拥有的文档,您实际上对与会者常见元素感兴趣,服务组件处理传输:

$uriAtt = 'http://www.webex.com/schemas/2002/06/service/attendee';
$xml->registerXPathNamespace('att', $uriAtt);
$uriCom = 'http://www.webex.com/schemas/2002/06/common';
$xml->registerXPathNamespace('com', $uriCom);
$result = $xml->xpath('//att:*|//com:*');
foreach ($result as $element) {
    $depth  = count($element->xpath("./ancestor::*[namespace-uri(.) = '$uriAtt' or namespace-uri(.) = '$uriCom']"));
    $indent = str_repeat('  ', $depth);
    printf("%s %s'n", $indent, $element->getName());
}

这次的示例输出:

 attendee
   person
     name
     firstName
     lastName
     address
       addressType
     phones
     email
     type
   contactID
   joinStatus
   meetingKey

那么,为什么要放弃所有的名称空间呢?它们可以帮助您获得感兴趣的元素。您也可以动态地进行

不要对数组进行泛型转换。只需加载并读取它。如果使用DOM+XPath,就没那么难了。

通用转换意味着您丢失了信息(名称空间)和功能(XPath)。

首先创建一个DOM并加载XML:

$dom = new DOMDocument();
$dom->loadXml($xml);

现在为DOM创建一个DOMXPath实例,并为名称空间注册前缀。这可以是XML文档中的前缀,也可以是不同的前缀。

$xpath = new DOMXPath($dom);
$xpath->registerNamespace('serv', 'http://www.webex.com/schemas/2002/06/service');
$xpath->registerNamespace('com', 'http://www.webex.com/schemas/2002/06/common');
$xpath->registerNamespace('att', 'http://www.webex.com/schemas/2002/06/service/attendee');

使用XPath表达式中注册的前缀来获取值和节点:

var_dump(
  $xpath->evaluate('string(/serv:message/serv:header/serv:response/serv:result)')
);

输出:

string(7) "SUCCESS"

获取所有attendee元素并输出名称:

foreach ($xpath->evaluate('/serv:message/serv:body/serv:bodyContent/att:attendee') as $attendee) {
  var_dump(
   $xpath->evaluate('string(att:person/com:name)', $attendee)
  );
};

输出:

string(10) "James Kirk"