Цикл XML с пространством имен в PHP - PullRequest
0 голосов
/ 10 апреля 2019

Я пытаюсь прочитать элементы xml с пространствами имен в PHP, но у меня возникают некоторые проблемы, возможно, из-за наличия нескольких дочерних узлов и из-за наличия пространств имен. На самом деле у меня нет проблем с файлами 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 голосов
/ 23 апреля 2019

Гораздо более упрощенная версия кода, использующая 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
0 голосов
/ 11 апреля 2019

Это SOAP, а не только XML.Наилучшим вариантом будет использование библиотеки SOAP.

Однако проблема, с которой вы сталкиваетесь, заключается в том, что строка foreach($info->xpath('//a:Expenses') as $header) назначает различные SimpleXMLElement экземпляры переменным $header, которые ничего не знают о регистрации, которую вы сделали для переменной $info.Вам нужно будет повторить регистрацию на каждом 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);
}

Или вы начнете использовать DOM.В DOM вы создаете отдельный объект для оценки выражений Xpath.Из-за этого вам нужно сделать регистрацию только один раз.Кроме того, вы можете использовать выражения 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));
}
...