Reputation: 635
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);
}
Upvotes: 0
Views: 91
Reputation: 635
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);
Upvotes: 0
Reputation: 19482
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"
}
Upvotes: 1