I have the following simple XML document:
<?xml version="1.0" encoding="UTF-8"?> <cars> <car> <data attrib="Make"> <text>Volvo</text> </data> <data attrib="Model"> <text>855</text> </data> </car> <car> <data attrib="Make"> <text>Volvo</text> </data> <data attrib="Model"> <text>745</text> </data> </car> <car> <data attrib="Make"> <text>Volvo</text> </data> <data attrib="Model"> <text>V70R</text> </data> </car> </cars> And the following XPath:
/cars/car/data[(@attrib='Model') and (text='855')] This returns the following result:
<data attrib="Model"><text>855</text></data> I want the XPath to return the whole <car> block for the match.
So return data would be like this:
<cars> <car> <data attrib="Make"> <text>Volvo</text> </data> <data attrib="Model"> <text>855</text> </data> </car> </cars> How would I modify the XPath expression above to achieve this?
2 Answers
XPath returns whatever node you go up to - in your case you're going to data, so that's what you're getting back. If you want car instead, place your predicate after car.
/cars/car[data/@attrib='Model' and data/text='855'] Or, slightly shorter
/cars/car[data[@attrib='Model' and text='855']] You can run it at this XMLPlayground.
XQuery to produce the desired output:
<cars> {/cars/car[data[@attrib='Model' and text='855']]} </cars> 3Here is a complete and likely one of the shortest possible XSLT solutions:
<xsl:stylesheet version="1.0" xmlns:xsl=""> <xsl:output omit-xml-declaration="yes" /> <xsl:template match="/*"> <cars> <xsl:copy-of select="car[data[@attrib='Model' and text='855']]"/> </cars> </xsl:template> </xsl:stylesheet> However, the following transformation, using the wellknown identity rule is both easier to write and provides maximum flexibility, extensibility and maintainability:
<xsl:stylesheet version="1.0" xmlns:xsl=""> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="car[not(data[@attrib='Model' and text='855'])]"/> </xsl:stylesheet> When either of these two transformations is applied on the provided XML document:
<cars> <car> <data attrib="Make"> <text>Volvo</text> </data> <data attrib="Model"> <text>855</text> </data> </car> <car> <data attrib="Make"> <text>Volvo</text> </data> <data attrib="Model"> <text>745</text> </data> </car> <car> <data attrib="Make"> <text>Volvo</text> </data> <data attrib="Model"> <text>V70R</text> </data> </car> </cars> the wanted, correct result is produced:
<cars> <car> <data attrib="Make"> <text>Volvo</text> </data> <data attrib="Model"> <text>855</text> </data> </car> </cars> Explanation:
The first transformation generates the top element
cars, then simply selects the wantedcarelement and copies it as the body ofcars.The second transformation is based on one of the most fundamental and powerful XSLT design patterns -- using and overriding the identity rule.
The identity template copies every matched node (for which it is selected to process) "as-is".
There is one template overriding the identity rule. This template matches any
carfor which it is not true thatdata[@attrib='Model' and text='855']. The body of the template is empty and this results in nothing from the matchedcarelement being copied to the output -- in other words we can say that amy matchingcarelement is "deleted".