在PHP

时间:2019-04-10 08:38:33

标签: php xml parsing namespaces

我正在尝试读取PHP中带有名称空间的xml元素,但是我可能由于存在多个子节点和名称空间而遇到了一些问题。实际上,没有命名空间的xml文件没有问题。

我想找到一种优雅的方法来在节点内循环信息,例如,我想读取元素“费用”内的所有数据(日期,商店,设计,价格等),并可能将其插入在数组中。 这是xml:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
  <Response xmlns="http://www.w3.org/2001/XMLSchema-instance">
     <Result xmlns:a="http://www.w3.org/2001/XMLSchema-instance" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <Success xmlns="http://www.w3.org/2001/XMLSchema-instance">OK</Success>
        <a:Name>John</a:Name>
        <a:Surname>Doe</a:Surname>

        <a:Expenses>

           <a:Expense>
              <a:Date>2019-01-01</a:Date>
              <a:Store>MALL1</a:Store>                 
              <a:Items>
                 <a:Item>
                    <a:IdItem>A1</a:IdItem>
                    <a:Price>5</a:Price>
                 </a:Item>
              </a:Items>
           </a:Expense>

           <a:Expense>
              <a:Date>2019-01-02</a:Date>
              <a:Store>MALL2</a:Store>                 
              <a:Items>
                 <a:Item>
                    <a:IdItem>A2</a:IdItem>
                    <a:Price>1</a:Price>
                 </a:Item>
                 <a:Item>
                    <a:IdItem>A3</a:IdItem>
                    <a:Price>3</a:Price>
                 </a:Item>
              </a:Items>
           </a:Expense>     

        </a:Expenses>
     </Result>
  </Response>

我尝试过这样的事情:

$info= new SimpleXMLElement($xml);
$info->registerXPathNamespace('s', 'http://example.com');
$info->registerXPathNamespace('a', 'http://www.w3.org/2001/XMLSchema-instance');

//Example 1:
foreach($info->xpath('//a:Name') as $header)
{
    echo (string) $header; 
}
//Result is: John                   

//Example 2:                    
foreach($info->xpath('//a:Expenses') as $header)
{
    $array = $header->xpath('//a:Expense/a:Store'); 
    print_r($array);
}

//Result is:
/*Array
(
[0] => SimpleXMLElement Object
    (
        [0] => MALL1
    )

[1] => SimpleXMLElement Object
    (
        [0] => MALL2
    )

[2] => SimpleXMLElement Object
    (
        [0] => MALL2
    )

)*/

//Example 3:
$array=$info ->xpath('/*/s:Body'); 
echo (string) $array[0]->Response->Result->Success;
//Result is: OK

但是,这当然不是最好的方法,因为我一次只能得到一个元素,并且无法进行适当的循环。 您将如何以更优雅,更正确的方式阅读此xml的各种元素? 谢谢。

2 个答案:

答案 0 :(得分:0)

这是SOAP,而不仅仅是XML。最好的选择是使用SOAP库。

不过,您面临的问题是,行foreach($info->xpath('//a:Expenses') as $header)SimpleXMLElement变量分配了不同的$header实例,这些实例对您在{{ 1}}变量。您需要在每个$info上重复注册。

SimpleXMLElement

或者您开始​​使用DOM。在DOM中,您将创建一个单独的对象来评估Xpath表达式。因此,您只需要注册一次。另外,您可以使用返回标量值的Xpath表达式。

$info= new SimpleXMLElement($xml);
$info->registerXPathNamespace('s', 'http://example.com');
$info->registerXPathNamespace('a', 'http://www.w3.org/2001/XMLSchema-instance');

foreach($info->xpath('//a:Expenses') as $header)
{
   $header->registerXPathNamespace('a', 'http://www.w3.org/2001/XMLSchema-instance');
   $array = $header->xpath('//a:Expense/a:Store'); 
   print_r($array);
}

答案 1 :(得分:0)

仅使用SimpleXML的代码的简化版本。基本思想是从<Result>(我也使用[0]来表示使用找到的第一个元素)元素开始,该元素包含您之后的所有数据。然后,而不是继续使用XPath,而是获取新定义的a名称空间中的所有子元素(使用children("a", true))。然后,这将使您无需任何前缀即可访问它们,因此可以使用标准的SimpleXML表示法-> ...

$info= new SimpleXMLElement($xml);
$info->registerXPathNamespace('a', 'http://www.w3.org/2001/XMLSchema-instance');

$result = $info->xpath("//a:Result")[0]->children("a", true);
//Example 1:
echo $result->Name.PHP_EOL;
//Result is: John

//Example 2:
foreach($result->Expenses->Expense as $header)
{
    echo (string)$header->Store.PHP_EOL;
}

输出...

John
MALL1
MALL2