Xquery filter by attribute and element

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 XPath to return the entire <car> block for compliance.

Thus, the returned data will be like this:

 <cars> <car> <data attrib="Make"> <text>Volvo</text> </data> <data attrib="Model"> <text>855</text> </data> </car> </cars> 

How do I change the XPath expression above to achieve this?

+6
source share
2 answers

XPath returns all the node you are going to - in your case, you are going to data so that you return. If you want car instead, put your predicate after car .

 /cars/car[data/@attrib='Model' and data/text='855'] 

Or a little shorter

 /cars/car[data[@attrib='Model' and text='855']] 

You can run it on this XMLPlayground .

XQuery to get the desired result:

 <cars> {/cars/car[data[@attrib='Model' and text='855']]} </cars> 
+13
source

Here is a complete and probably one of the shortest possible XSLT solutions:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <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 conversion using a known rule easier to write and provides maximum flexibility, extensibility and maintainability:

  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <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 one of these two transformations is applied to 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 desired, correct result is output:

 <cars> <car> <data attrib="Make"> <text>Volvo</text> </data> <data attrib="Model"> <text>855</text> </data> </car> </cars> 

Explanation

  • The first conversion generates the top element of cars , then simply selects the desired element car and copies it as the body of cars .

  • The second transformation is based on one of the most fundamental and powerful XSLT design patterns β€” the use and redefinition of an identification rule.

  • An identity template copies each associated node (for which it is selected for processing) "as is".

  • There is one template that overrides the identification rule. This pattern matches any car for which it is not true that data[@attrib='Model' and text='855'] . The body of the template is empty, and this does not mean that the matching car element is copied to the output - in other words, we can say that the element corresponding to the car element is "deleted".

+3
source

Source: https://habr.com/ru/post/919732/


All Articles