0

I posted another question, which to my error was incomplete. I have an XML with multiple namespaces, but I need to access the values of in a foreach.

My XML array is:

<ArrayOfSession xmlns:i='http://www.w3.org/2001/XMLSchema-instance' xmlns='http://schemas.datacontract.org/2004/07/Vista.Online.BackOffice.Api.Models.V1'> 
<Session>
  <AreComplimentariesAllowed>true</AreComplimentariesAllowed>
  <Attributes xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <d3p1:string>0000000009</d3p1:string>
    <d3p1:string>0000000011</d3p1:string>
  </Attributes>
</Session>
</ArrayOfSession>

I was helped by another user at: Retrieving an array of values from an XML content with a namespace in PHP

With that answer I have tried a few variations. This being my current one, which isn't working:

$xml->registerXPathNamespace('i', 'http://www.w3.org/2001/XMLSchema-instance');
$xml->registerXPathNamespace('j', 'http://schemas.datacontract.org/2004/07/Vista.Online.BackOffice.Api.Models.V1');
$xml->registerXPathNamespace('ns', 'http://schemas.microsoft.com/2003/10/Serialization/Arrays');

foreach($xml->xpath('//ns:d3p1') as $header){
    var_export($header->xpath('//Attributes/ns:string'));
}

I also tried:

foreach($xml->xpath('//ns:d3p1') as $header){
        var_export($header->xpath('//ns:string'));
    }

This is stumping me!

In my current working version for the rest of code, I'm retrieving the elements with:

foreach($xml->Session as $event){
    // Do Something with $event->AreComplimentariesAllowed
}

This works fine, but I just can't access those Attributes elements.

Thanks as ever.

Community
  • 1
  • 1
David
  • 369
  • 2
  • 4
  • 14

1 Answers1

0

You have to register the namespaces it on each SimpleXMLElement before you call xpath(). You register them on the $xml variable, but not on $header.

Your XML has an default namespace, too. You do register the prefix j for it, but you do not use the prefix in the expressions. Last here are no elements d3p1. This is only a namespace prefix in the document.

So to iterate the Session elements and then the string values inside the Attributes

$sessions = new SimpleXMLElement($xml);
$sessions->registerXPathNamespace(
  'j', 
  'http://schemas.datacontract.org/2004/07/Vista.Online.BackOffice.Api.Models.V1'
);

foreach($sessions->xpath('//j:Session') as $session) {
  $session->registerXPathNamespace(
    'j', 
    'http://schemas.datacontract.org/2004/07/Vista.Online.BackOffice.Api.Models.V1'
  );
  $session->registerXPathNamespace(
    'ns', 
    'http://schemas.microsoft.com/2003/10/Serialization/Arrays'
  );
  var_export($session->xpath('.//j:Attributes/ns:string'));
}

In DOM this looks different:

$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
$xpath->registerNamespace(
  'j', 
  'http://schemas.datacontract.org/2004/07/Vista.Online.BackOffice.Api.Models.V1'
);
$xpath->registerNamespace(
  'ns', 
  'http://schemas.microsoft.com/2003/10/Serialization/Arrays'
);

foreach ($xpath->evaluate('//j:Session') as $session) {
  var_export(
    iterator_to_array($xpath->evaluate('.//j:Attributes/ns:string', $session))
  );
}

It has a dedicated Xpath object, so the namespace registration has to be done only once.

ThW
  • 19,120
  • 3
  • 22
  • 44
  • Thanks. Although I'm still a little confused. Is this not registering them first? `$xml->registerXPathNamespace('i', 'http://www.w3.org/2001/XMLSchema-instance'); $xml->registerXPathNamespace('j', 'http://schemas.datacontract.org/2004/07/Vista.Online.BackOffice.Api.Models.V1'); $xml->registerXPathNamespace('ns', 'http://schemas.microsoft.com/2003/10/Serialization/Arrays');` – David Nov 22 '16 at 10:38
  • Only on the SimpleXMLElement represented by the `$xml` variable, but you call the method on the $header variable. In SimpleXML you will have to register the namespaces again and again on each variable. Here is an example: http://stackoverflow.com/a/29556930/2265374 – ThW Nov 22 '16 at 13:02
  • Thanks. I can see the relationship, but can't piece it together: https://eval.in/683080 – David Nov 22 '16 at 16:01
  • I rewrote my answer, you missed the default namespace. – ThW Nov 23 '16 at 09:48
  • Perfect. I can see where I went wrong and what I need to reference at which point. It goes without saying that I appreciate the time you've taken to guide me through it - thanks. – David Nov 23 '16 at 09:56