0

Here's my problem. I want to create a function that takes an outside variable that contains an xpath and once the function runs I want to add to that same variable to create a counter.

So I have the outside variable:

 $node = $xmlDoc->xpath('//a:Order');

Then the function with a single argument that will take the outside variable ($node). Like so:

function loopXML($node) {
    i=1; //counter variable
}

Now I want to add a counter to $node so that it goes through all of the children of "Order". Outside of the function, I would use:

$child = $xmlDoc->xpath('//a:Order['.$i.']/*'); 

But inside of the function, I have no idea how to concat it. Does anyone have any idea how I could do this?

EDIT: Also, it should be noted that I created an arbitrary namespace already:

 foreach($xmlDoc->getDocNamespaces() as $strPrefix => $strNamespace) {
     if(strlen($strPrefix)==0) {
         $strPrefix="a"; //Assign an arbitrary namespace prefix.
     }
     $xmlDoc->registerXPathNamespace($strPrefix,$strNamespace);
 }
6
  • Why are you passing $node into the function? How is it used? Commented Nov 15, 2016 at 18:03
  • The goal of the function is to convert xml to csv. $node is xml parent xml tag to cycle through. $child is all of the children of $node. I'm having to do this a lot, so I'd like to create a function to reduce redundancy and so I can just include the xpath (or xml tag) that I want to pull. Commented Nov 15, 2016 at 18:05
  • Also, why not use the $xml->children() property on your original node so you can iterate them? Commented Nov 15, 2016 at 18:05
  • Because I didn't know about it! Thanks! I'll give that a try and see if it works. I'll post if it does. Commented Nov 15, 2016 at 18:08
  • I tried $nodeC = $nodeP->childNodes; within the function, but it didn't work. Commented Nov 15, 2016 at 18:13

2 Answers 2

1

SimpleXMLElement::xpath() uses the node associated with the SimpleXML element as the context so you can do something like:

foreach ($xmlDoc->xpath('//a:Order') as $order) {
  foreach ($order->xpath('*') as $field) {
    ...
  }
} 

But SimpleXMLElement::children() is a list of the element child nodes so it returns the same as the Xpath expression * or to be more exact '*[namespace-uri == ""]'. The first argument is the namespace of the children you would like to fetch.

foreach ($xmlDoc->xpath('//a:Order') as $order) {
  foreach ($order->children() as $field) {
    ...
  }
}

This can be easily refactored into a function.

function getRecord(SimpleXMLelement $order, $namespace) {
  $result = [];
  foreach ($order->children($namespace) as $field) {
    $result[$field->getName()] = (string)$field;
  }
  return $result;
}

You should always depend on the actual namespace, never on the prefix. Prefixes can change and are optional.

Put all together:

$xml = <<<'XML'
<a:orders xmlns:a="urn:a">
  <a:order>
    <a:foo>bar</a:foo>
    <a:answer>42</a:answer>
  </a:order>
</a:orders>
XML;

$namespace = 'urn:a';

$orders = new SimpleXMLElement($xml);
$orders->registerXpathNamespace('a', $namespace);

function getRecord(SimpleXMLelement $order, $namespace = NULL) {
  $result = [];
  foreach ($order->children($namespace) as $field) {
    $result[$field->getName()] = (string)$field;
  }
  return $result;
} 

foreach ($orders->xpath('//a:order') as $order) {
  var_dump(getRecord($order, $namespace));
}

Output:

array(2) {
  ["foo"]=>
  string(3) "bar"
  ["answer"]=>
  string(2) "42"
}
Sign up to request clarification or add additional context in comments.

2 Comments

I tried this, but didn't seem to get it to work. It may be worth noting that I have created an arbitrary namespace already.
One of the limits is that you will have to register namespaces on each SimpleXMLElement you call xpath() if you use them in the expression.
0

So I figured it out with a lot of Googling and the help of ThW. So to all that helped, thank you. Here's how I got it to work:

 $orderPNode = '//a:Order'; 
 $amazonRawXML = 'AmazonRaw.xml';
 $amazonRawCSV = 'AmazonRaw.csv';


function loopXML($xmlDoc, $node, $writeCsv) {
$i = 1;
$xmlDocs = simplexml_load_file($xmlDoc);
$result = [];

foreach($xmlDocs->getDocNamespaces() as $strPrefix => $strNamespace) {
    if(strlen($strPrefix)==0) {
        $strPrefix="a"; //Assign an arbitrary namespace prefix.
    }
$xmlDocs->registerXPathNamespace($strPrefix,$strNamespace);
}

file_put_contents($writeCsv, ""); // Clear contents of csv file after each go

$nodeP = $xmlDocs->xpath($node); 

foreach ($nodeP as $n) {
    $nodeC = $xmlDocs->xpath($node.'['.$i.']/*');
    if($nodeC) {
        foreach ($nodeC as $value) {
            $values[] = $value;
        }
    $write = fopen($writeCsv, 'a'); 
    fputcsv($write, $values);
    fclose($write);

    $values = [];
    $i++;
    } else {
        $result[] = $n;
        $i++;
    }

}
return $result;
}
loopXML($amazonRawXML, $orderPNode, $amazonRawCSV);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.