I mean to add an attribute element to a node if and only if the source for that attribute's data exists.
In other words, I don't want to end up with empty attributes in case the source isn't matched by my rule.
<tok id="t1" fooID=""/> //not accepted
<tok id="t1" /> //instead of ^
<tok id="t1" fooID="bar"/> //accepted
foo attribute with "source2". Here's what I'm currently using:
<xsl:template match="tokens/token">
<tok id="{@ID}"
ctag="{/root/myStuff/fooSources[1and2 how?]/fooSource[@fooID=current()/@ID]}"
>
</tok>
The source XML is like this:
<root>
<myStuff>
<tokens>
<token ID="bar"/>
<token ID="anotherBar"/>
<token ID="noFoo"/>
</tokens>
<fooSources>
<fooSource fooID="bar"> kitten </fooSource>
<fooSource fooID="anotherBar"> shovel </fooSource>
</fooSources>
<fooSources>
<fooSource fooID="bar"> kitty </fooSource>
<fooSource fooID="notAnotherBar"> fridge </fooSource>
</fooSources>
</myStuff>
</root>
The desired result would be this:
<tok id="bar" fooID="kitten" fooID_2="kitty"/>
<tok id="anotherBar" fooID="shovel"/>
<tok id="noFoo" />
Thanks in advance for any help!
PS: I'd like to do this in xpath 1.0
<foo>
<xsl:if test="@bar">
<xsl:attribute name="id">
<xsl:value-of select="@bar"/>
</xsl:attribute>
</xsl:if>
</foo>
PS: I'd like to do this in xpath 1.0
XPath is a query language for XML documents and as such it cannot modify any nodes of the document.
To produce the wanted result (changing element names and adding new attributes to elements) you have to use another language that is hosting XPath. The most appropriate such language, created especially with the goal to be used for XML transformations is XSLT.
This XSLT 1.0 transformation:
<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:key name="kFooIdByVal" match="fooSource/@fooID"
use="."/>
<xsl:template match="token">
<tok>
<xsl:copy-of select="@*"/>
<xsl:apply-templates
select="key('kFooIdByVal',@ID)"/>
</tok>
</xsl:template>
<xsl:template match="@fooID">
<xsl:variable name="vattrName"
select="concat('fooID',
substring(concat('_',position()),
1 div (position() >1)
)
)
"/>
<xsl:attribute name="{$vattrName}">
<xsl:value-of select="normalize-space(..)"/>
</xsl:attribute>
</xsl:template>
<xsl:template match="fooSources"/>
</xsl:stylesheet>
when applied on the provided XML document:
<root>
<myStuff>
<tokens>
<token ID="bar"/>
<token ID="anotherBar"/>
<token ID="noFoo"/>
</tokens>
<fooSources>
<fooSource fooID="bar"> kitten </fooSource>
<fooSource fooID="anotherBar"> shovel </fooSource>
</fooSources>
<fooSources>
<fooSource fooID="bar"> kitty </fooSource>
<fooSource fooID="notAnotherBar"> fridge </fooSource>
</fooSources>
</myStuff>
</root>
produces the wanted, correct result:
<tok ID="bar" fooID="kitten" fooID_2="kitty"/>
<tok ID="anotherBar" fooID="shovel"/>
<tok ID="noFoo"/>
Explanation:
A template matching any token element creates a tok element that has all the existing attributes (if any) of the matchedtokenelement. It also applies templates on anyfooIDattribute, whose value is the same as the value of theIDattribute of the current (matched) node. If there isn't any suchfooID` attribute, no futher processing is done and no additional attributes are created.
The template that matches a fooID attribute must generate a new attribute of the form "fooID_{N}" where N is the position of the current node (the matched fooID attribute) in the node-list (created by the <xsl:apply-templates> instruction that selected this current template for applying on this fooID attribute). If N is 1, we do not append to the start of the name ("fooID") the string "_"{N}.
To avoid the side-effects of default XSLT processing we add a third template that matches any fooSources element and, (the template) has an empty body.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With