I have a piece of XML where the same information can appear as a child of different nodes. Such as :
<root>
<category id=1>
<product id="ABC123" >
<sizes>
<size name="S"/>
<size name="M"/>
<size name="L"/>
<size name="XL"/>
<size name="2XL"/>
<size name="3XL"/>
</sizes>
</product>
</products>
</category>
<category id=2>
<products>
<product id="ABC123" >
<sizes>
<size name="S"/>
<size name="M"/>
<size name="L"/>
<size name="XL"/>
<size name="2XL"/>
<size name="3XL"/>
</sizes>
</product>
<product id="PPP543" >
<sizes>
<size name="S"/>
<size name="M"/>
<size name="L"/>
<size name="XL"/>
</sizes>
</product>
</products>
</category>
My goal is to select the sizes of product id ABC123 and store them as an array. The current code that I have is :
$arrTest=array();
foreach($xml->xpath('//root/category/products/product[@id= "'.$productCall.'" ]/sizes/size') as $size){
array_push($arrTest, $size["name"]);
}
$productCall is the id I am looking for. In this case it's ABC123.
The output is S,M,L,XL,2XL,3XL,S,M,L,XL,2XL,3XL. Meaning that it is reading the two entries that were found. I expected this given the foreach loop, but I can't seem to find a way to just get the output of the first result. I have tried adding [0] and [1] :
$y=$xml->xpath('//root/category/products/product[@id= "'.$productCall.'" ][1]/sizes/size');
[0] returns nothing and [1] returns the same results I'm already getting.
I'm hoping this is a simple matter of me missing something something basic or just over-thinking, as I really haven't worked with xpath before.
Use:
(/*/*//product[@id='ABC123']/sizes)[1]/size
Or use:
((/*/*/product | /*/*/products/product)/sizes)[1]/size
XSLT - based verification:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select=
"(/*/*//product[@id='ABC123']/sizes)[1]/size"/>
========
<xsl:copy-of select=
"((/*/*/product | /*/*/products/product)/sizes)[1]/size"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied to the provided XML document (after correcting it into a well-formed one):
<root>
<category id="1">
<product id="ABC123" >
<sizes>
<size name="S"/>
<size name="M"/>
<size name="L"/>
<size name="XL"/>
<size name="2XL"/>
<size name="3XL"/>
</sizes>
</product>
</category>
<category id="2">
<products>
<product id="ABC123" >
<sizes>
<size name="S"/>
<size name="M"/>
<size name="L"/>
<size name="XL"/>
<size name="2XL"/>
<size name="3XL"/>
</sizes>
</product>
<product id="PPP543" >
<sizes>
<size name="S"/>
<size name="M"/>
<size name="L"/>
<size name="XL"/>
</sizes>
</product>
</products>
</category>
</root>
the two XPath expressions are evaluated and the selected nodes from each one, well visually delimited, are copied to the output -- both correct:
<size name="S"/>
<size name="M"/>
<size name="L"/>
<size name="XL"/>
<size name="2XL"/>
<size name="3XL"/>
========
<size name="S"/>
<size name="M"/>
<size name="L"/>
<size name="XL"/>
<size name="2XL"/>
<size name="3XL"/>
Do note: I expect the second XPath expression to be more efficient (on sufficiently large documents), because it doesn't use the //
pseudo-operator.