How to get all results if there are multiple matches : XSL
-
04-12-2019 - |
Question
I'm practicing some XSL and using this XML document as a simple example:
<?xml version="1.1" encoding="UTF-8"?>
<zoo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="zoo.xsd" >
<animals>
<animal type="lion">
<name>Zeus</name>
<gender>M</gender>
<eats>antelope</eats>
<eats>monkey</eats>
</animal>
<animal type="monkey">
<name>Fredo</name>
<gender>M</gender>
<eats>banana</eats>
<iseatenby>lion</iseatenby>
</animal>
<animal type="lion">
<name>Here</name>
<gender>F</gender>
<eats>antelope</eats>
<eats>monkey</eats>
</animal>
<animal type="antelope">
<name>Annie</name>
<gender>F</gender>
<eats>grass</eats>
<iseatenby>lion</iseatenby>
</animal>
<animal type="elephant">
<name>Moses</name>
<gender>M</gender>
<eats>leaves</eats>
</animal>
</animals>
</zoo>
I've been able to get some basic info via my XSL doc, but I'm stuck on one thing right now: how can I get all of the results if there are more than one? For example, in my document, some animals have multiple "eats" elements. I want to display them in a comma-separated string; eventually I want to transform each animal's elements into attributes and just have a single attribute for each. (Using my previous example, the new animal element lion's "eats" attribute would look like this: eats="antelope, monkey"
)
Could someone please explain how I would do something like this with XSL?? Any help is much appreciated. :)
Solution
Use this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="zoo/animals">
<xsl:copy>
<xsl:apply-templates select="animal"/>
</xsl:copy>
</xsl:template>
<xsl:template match="animal">
<xsl:copy>
<xsl:attribute name="eats">
<xsl:for-each select="eats">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Reference:
The position function returns a number equal to the context position from the expression evaluation context. The last function returns a number equal to the context size from the expression evaluation context.
xsl:if
checks if the current node isn't last node in context. If so, outputs ,
.
OTHER TIPS
not a perfect though :)
try it now hope it helps .. I am converting each element as attribute ..
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//animal">
<xsl:copy>
<!--copies all other elements as attributes-->
<xsl:for-each select="*[name()!='eats']">
<xsl:attribute name="{name()}">
<xsl:apply-templates select = "text()"/>
</xsl:attribute>
</xsl:for-each>
<xsl:attribute name="eats">
<!-- Go to <eats/> node -->
<xsl:for-each select="eats">
<!--insearts string ", " if it has preceding values already :) -->
<xsl:if test="preceding-sibling::eats">
<xsl:text>, </xsl:text>
</xsl:if>
<!--copies all the text :) -->
<xsl:apply-templates select="text()"/>
</xsl:for-each>
</xsl:attribute>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>