I have an inout and I want apply template for only specific elements.
Input:
<p>patagaram 1<italic>weas</italic>(<xref ref-type="bibr" rid="R3"><italic>3</italic></xref>) (<xref ref-type="fig" rid="F1">Figures 1</xref> and <xref ref-type="fig" rid="F2">2</xref>).</p>
Tried code:
<p type="{$type}">
<xsl:if test="$id">
<xsl:attribute name="id" select="$id"/>
</xsl:if>
<xsl:apply-templates select="p"/>
What I want:
I want to apply template except xref[#ref-type='fig'] which is inside p. How can I do this.
I am using XSLT 2.0
I'm afraid that it is not obvious what your intention is.
If you want to exclude the element from the output, an empty template as #Ajeet Singh proposes, would be the solution.
In XSLT a template rule applies only to the contents of its #match attribute.
If you want to define a kind of global template that applies to all elements except xref inside p, you could also write:
<xsl:template match="*[not(self::xref[parent::p])]">
** do something **
</xsl:template>
Related
I have a series of templates which are processing various type of mapping elements differently. But, I want a similar set of attributes to be added to the generated element. Is there a way to do this with an XSLT function, or is there another recommended way?
Here is an example of a mapping template, this one is for ones that have no source values. Those three attributes that are added to the generated element, I don't want to duplicate in each mapping template. Is there a way to avoid doing that?
<xsl:template match="mapping[source/not(*)]">
<xsl:element name="{{destination/attribute/text()}}" namespace="{{destination/attribute/#namespace}}">
<xsl:attribute name="mapping-key"><xsl:value-of select="generate-id(.)"/></xsl:attribute>
<xsl:attribute name="override"><xsl:value-of select="(#override, 'false')[1]"/></xsl:attribute>
<xsl:attribute name="single-value"><xsl:value-of select="(#single-value, 'false')[1]"/></xsl:attribute>
<!-- add custom stuff to the element specific to this template -->
</xsl:element>
</xsl:template>
Thanks,
-tj
You can indeed use xsl:attribute-set here, to allow you to re-use groups of attributes in different places.
For example, see this XSLT (I removed the namespace creation for brevity)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:attribute-set name="attrSet">
<xsl:attribute name="mapping-key" select="generate-id(.)"/>
<xsl:attribute name="override" select="(#override, 'false')[1]"/>
<xsl:attribute name="single-value" select="(#single-value, 'false')[1]"/>
</xsl:attribute-set>
<xsl:template match="mapping[source/not(*)]">
<xsl:element name="{destination/attribute/text()}" use-attribute-sets="attrSet">
<!-- add custom stuff to the element specific to this template -->
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Note, I have used the slightly simplified format for creating attributes (with select on the xsl:attribute statement) as available in XSLT 2.0.
Also, I note, you used double-curly braces in creating the element names {{destination/attribute/text()}}", where it should only be single, but I am guessing you may be doing some text pre-processing of the XSLT before using it, perhaps?
I am trying to re-use a XSL template, and place other templates within this template, multiple times.
Here's an example of my code:
<xsl:template name="wrapper">
<div>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template name="template1"></xsl:template>
<xsl:template name="template2"></xsl:template>
So, now i want to apply both template 1 and template 2 inside template 'wrapper', something like this (I know this isn't the right code, but the idea is there):
<xsl:template name="template1">
<xsl:template match="wrapper">
<!--code here-->
</xsl:template>
</xsl:template>
<xsl:template name="template2">
<xsl:template match="wrapper">
<!--code here-->
</xsl:template>
</xsl:template>
Any help on this would be grealty appreciated.
It is syntactically illegal to nest a template definition into another.
As per the W3C XSLT (both 1.0 and 2.0) specification, an xsl:template must be a child of the top element xsl:stylesheet.
This means that all templates in a stylesheet module must be siblings.
The way to invoke a named template is to use the xsl:call-template instruction like this:
<xsl:call-template name="someTemplateName">
<!-- Possibly place one or more `xsl:with-param` elements here -->
</xsl:call-template>
However, beaware that:
It is a good style and more in the spirit of XSLT to use unnamed templates (that have a match attribute) and to select the best matching template with an xsl:apply-templates instruction.
Most of the answers to SO XSLT questions demonstrate the use of xsl:apply-templates.
So, now i want to apply both template 1 and template 2 inside template 'wrapper',
If I take this literally:
<xsl:template name="wrapper">
<xsl:call-template name="template1" />
<xsl:call-template name="template2" />
</xsl:template>
But I have a strong gut feeling that you're somehow shooting yourself in the foot here.
want to make a comma-delimited string from a list of 3 possible attributes of an element.
I have found a thread here:
XSLT concat string, remove last comma
that describes how to build a comma-delimited string from elements. I want to do the same thing with a list of attributes.
From the following element:
<myElement attr1="Don't report this one" attr2="value1" attr3="value2" attr4="value3" />
I would like to produce a string that reads: "value1,value2,value3"
One other caveat: attr2 thru attr4 may or may not have values but, if they do have values, they will go in order. So, attr4 will not have a value if attr3 does not. attr3 will not have a value if attr2 does not. So, for an attribute to have a value, the one before it in the attribute list must have a value.
How can I modify the code in the solution to the thread linked to above so that it is attribute-centric instead of element-centric?
Thanks in advance for whatever help you can provide.
This is easy in principe, but only if it is really clear which attribute you want to exclude. Since attributes are not by definition ordered in XML (in contrast the elements), you need to say how the attribute(s) to skip can be identified.
Edit: Regarding the attribute order, XML section 3.1 says:
Note that the order of attribute specifications in a start-tag or empty-element tag is not significant.
That said, something like this should do the trick (adjust the [] condition as you see fit):
<xsl:template match="myElement">
<xsl:for-each select="#*[position()!=1]">
<xsl:value-of select="." />
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
</xsl:template>
In XSLT 2.0 it is as easy as
<xsl:template match="myElement">
<xsl:value-of select="#* except #att1" separator=","/>
</xsl:template>
I would appreciate Lucero's answer .. He definitely has nailed it ..
Well, Here is one more code which truncates attribute attr1, which appears at any positions other than 1 in attribute list.
scenario like this::
<myElement attr2="value1" attr3="value2" attr4="value3" attr1="Don't report this one" />
Here is the XSLT code .. ::
<xsl:template match="myElement">
<xsl:for-each select="#*[name()!='attr1']">
<xsl:value-of select="." />
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
</xsl:template>
The output will be:
value1,value2,value3
If you wish to omit more than one attribute say .. attr1 and attr2 ..
<xsl:template match="myElement">
<xsl:for-each select="#*[name()!='attr1' and name()!='attr2']">
<xsl:value-of select="." />
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
</xsl:template>
The corresponding output will be:
value2,value3
How can I output all of the text in a node, including the text in its children nodes while excluding the text in "a" nodes?
Make use of the built-in template rule for text nodes, which is to copy them to the result. Even for a new processing mode that you specify ("all-but-a" in the code below), the built-in rules will work: for elements, (recursively) process children; for text nodes, copy. You only need to override one of them, the rule for <a> elements, hence the empty template rule, which effectively strips out the text.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="myNode">
<!-- Process children -->
<xsl:apply-templates mode="all-but-a"/>
</xsl:template>
<!-- Don't process <a> elements -->
<xsl:template mode="all-but-a" match="a"/>
</xsl:stylesheet>
For a complete description of how the built-in template rules work, check out the "Built-in Template Rules" section of "How XSLT Works" on my website.
if you're currently processing your node.
<xsl:value-of select="."/>
should return all the textual content
I believe this is what you're looking for:
<xsl:for-each select="//text()[not(ancestor::a)]">
<xsl:value-of select="."/>
</xsl:for-each>
It selects all text nodes that are not children of anchor tags.
<xsl:for-each select="//*[text() and name() != 'a']">
<xsl:value-of select="."/>
</xsl:for-each>
How can I, with XSLT, select nodes based on a substring of the nodes' element name?
For example, consider the XML:
<foo_bar>Keep this.
<foo_who>Keep this, too.
<fu_bar>Don't want this.</fu_bar>
</foo_who>
</foo_bar>
From which I want to output:
<foo_bar>Keep this.
<foo_who>Keep this, too.
</foo_who>
</foo_bar>
Here I want to select for processing those nodes whose names match a regex like "foo.*".
I think I need an XSLT template match attribute expression, or an apply-templates select attribute expression, that applies the regex to the element's name. But maybe this can't be done without some construct like an statement?
Any help would be appreciated.
Here is some XSL that finds elements that start with "foo" to get you started. I don't think regex functionality was added until XSLT 2.0 based on Regular Expression Matching in XSLT 2.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<xsl:variable name="name" select="local-name()"/>
<xsl:if test="starts-with($name, 'foo')">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
It gives this output, which seems to have an extra newline.
<foo_bar>Keep this.
<foo_who>Keep this, too.
</foo_who>
</foo_bar>