简体   繁体   English

在PHP中使用命名空间循环xml

[英]Loop xml with namespace in PHP

I'm trying to read the elements of an xml with namespaces in PHP, but I'm having some issues maybe for the presence of several child nodes and for the presence of namespaces. 我正在尝试在PHP中读取带有命名空间的xml元素,但是我遇到了一些问题,可能存在多个子节点以及存在命名空间。 In fact, I have no issues with xml files without namespaces. 事实上,没有名称空间的xml文件没有问题。

I would like to find an elegant way to loop the information inside the nodes, for instance I would like to read all the data (date, store, iditem, price..) inside the elements 'Expense' and possibly inserting them inside an array. 我想找到一种优雅的方式来循环节点内的信息,例如我想读取元素'Expense'中的所有数据(date,store,iditem,price ..)并可能将它们插入到数组中。 Here is the 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>

I tried something like this: 我试过这样的事情:

$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

But, of course, this is not the best way, because I get only one element at a time and can't make a proper loop. 但是,当然,这不是最好的方法,因为我一次只能获得一个元素而无法进行正确的循环。 How would you read the various elements of this xml in a more elegant and correct way? 你会如何以更优雅和正确的方式阅读这个xml的各种元素? Thank you. 谢谢。

This is SOAP, not just XML. 这是SOAP,而不仅仅是XML。 The best option would be to use a SOAP library. 最好的选择是使用SOAP库。

However the problem you're facing is that the line foreach($info->xpath('//a:Expenses') as $header) assigns different SimpleXMLElement instances to the $header variables that do not know anything about the registration that you did on the $info variable. 但是你遇到的问题是foreach($info->xpath('//a:Expenses') as $header)将不同的SimpleXMLElement实例分配给$header变量,这些变量对你的注册一无所知在$info变量上做了。 You would need to repeat the registration on each SimpleXMLElement . 您需要在每个SimpleXMLElement上重复注册。

$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);
}

Or you start using DOM. 或者你开始使用DOM。 In DOM you create an separate object to evaluate Xpath expressions. 在DOM中,您可以创建一个单独的对象来评估Xpath表达式。 Because of that you only need to do the registration once. 因此,您只需要进行一次注册。 Additionally you can use Xpath expressions that return scalar values. 此外,您可以使用返回标量值的Xpath表达式。

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
$xpath->registerXPathNamespace('s', 'http://example.com');
$xpath->registerXPathNamespace('a', 'http://www.w3.org/2001/XMLSchema-instance');

foreach($xpath->evaluate('//a:Expenses/a:Expense') as $expense)
{
   print_r($xpath->evaluate('string(a:Store)', $expense));
}

A much more simplified version of the code just using SimpleXML. 仅使用SimpleXML的更简化的代码版本。 The basic idea is to start with the <Result> (I also use [0] to indicate to use the first element found) element which contains all of the data your after. 基本思路是从<Result> (我也使用[0]指示使用找到的第一个元素)元素开始,该元素包含您之后的所有数据。 Then instead of keep on using XPath, fetch all of the child elements in the newly defined a namespace (using children("a", true) ). 然后,不是继续使用XPath,而是获取新定义a命名空间中的所有子元素(使用children("a", true) )。 This will then allow you to access them without any prefix and so use the standard SimpleXML notation -> ... 这将允许您在没有任何前缀的情况下访问它们,因此使用标准的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;
}

which outputs... 哪个输出......

John
MALL1
MALL2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM