XSLT get a variable in case of element name - xslt

I can get an xml with one of three nodes.
<root>
<headerA>
<!-- Content -->
</headerA>
</root>
in place of <headerA> there can be <headerB> or <headerŠ”> node with similar content. In this case I want to have html output as:
<span> Type [X] </span>
Where [X] is A, B or C depending on the tag name of the element.

As Tomalak has already observed, your question is rather vague. It sounds as if you might be asking how to write either a single template for the three kinds of header:
<xsl:template match="headerA | headerB | headerC">
<span>
<xsl:apply-templates/>
</span>
</xsl:template>
or else how to write similar but distinct templates for them:
<xsl:template match="headerA">
<span> Type A </span>
</xsl:template>
<xsl:template match="headerB">
<span> Type B </span>
</xsl:template>
<!--* Template for headerC left as an exercise for the reader *-->
or else how to write a single template that does slightly different things based on what it matches:
<xsl:template match="headerA | headerB | headerC">
<span>
<xsl:choose>
<xsl:when test="self::headerA">
<xsl:text> Type A </xsl:type>
</xsl:when>
<xsl:when test="self::headerB">
<xsl:text> Type B </xsl:type>
</xsl:when>
<!--* etc. *-->
<xsl:otherwise>
<xsl:message terminate="yes"
>I thought this could not happen.</xsl:message>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates/>
</span>
</xsl:template>
If you figure out which of these helps you, you will be closer to understanding what question you are trying to ask.

<xsl:template match="*[starts-with(name(), 'header')]">
<xsl:variable name="type" select="substring-after(name(), 'header')" />
<span>
<xsl:value-of select="concat(' Type ', $type, ' ')" />
</span>
</xsl:template>
This would work for any element named headerX, where can be X any string.

Related

Inline element data not appearing where expected after XSL transform

With the following XML
<para>Refer to Table 3 and Figure <grphcref refid="apm00-02-02-000018" shownow="0">6</grphcref>
for the door dimensions and clearances.</para>
and this XSL:
<xsl:template match="prcitem">
<xsl:for-each select="para">
<p>
<xsl:value-of select="." />
<xsl:apply-templates select="./grphcref" />
</p>
</xsl:for-each>
<xsl:apply-templates select="grphcref" />
<xsl:apply-templates select="table" />
<xsl:apply-templates select="unlist" />
</xsl:template>
<xsl:template match="grphcref">
<xsl:variable name="gotoimg" select="concat('#',#refid)"/>
<a href="{$gotoimg}" >
<xsl:value-of select="." /> - <xsl:value-of select="#refid" /> </a>
</xsl:template>
I get:
<p>Refer to Table 3 and Figure 6 for the door dimensions and clearances.
6 - apm00-02-02-000018
when I expected:
<p>Refer to Table 3 and Figure 6 - apm00-02-02-000018
for the door dimensions and clearances.
Can anyone offer guidance as to where I went wrong?
thx
If a para element is supposed to be transformed to a p element then in my view the "natural" way in XSLT is a template
<xsl:template match="para">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
If any other elements need special treatment add a template e.g.
<xsl:template match="grphcref">
<xsl:variable name="gotoimg" select="concat('#',#refid)"/>
<a href="{$gotoimg}" >
<xsl:value-of select="." /> - <xsl:value-of select="#refid" /> </a>
</xsl:template>
Text nodes are copied through to the result by the built-in templates.
The input sample doesn't explain why you mix template matching and for-each and why or whether you need xsl:apply-templates with any particularly selected nodes; as long as the input order should be preserved a simple processing of all child nodes with <xsl:apply-templates/> should suffice.

different cases differentiate and link them

I've the below XML.
<orderedlist type="manual">
<item num="1."><para>identify the issues in dispute;</para></item>
<item num="1.1"><para>explore and generate options;</para></item>
<item num="1.1.1"><para>communicate with one another; and/or</para></item>
<item num="(i)"><para>copy of the cancellation of employment pass; and</para></item>
<orderedlist>
here i want to translate all the . to - in item-num and surround them with a different tag, where there is some number or period after . previously I've received a solution and I've used not(ends-with(./#num,'.')). That worked fine till some point, recently I've came across a situation(4th item num in the above XML) where in the item num has values like (i), i, a, a), and these also satisfy the condition and are getting the new tag. below is my XSL template.
<xsl:template name="orderedlist" match="orderedlist">
<ol class="eng-orderedlist orderedlist">
<xsl:apply-templates/>
</ol>
</xsl:template>
<xsl:template match="item">
<xsl:call-template name="paran"></xsl:call-template>
</xsl:template>
<xsl:template name="paran">
<xsl:apply-templates select="./para/node()[1][self::page]" mode="first"/>
<li class="item">
<div class="para">
<xsl:choose>
<xsl:when test="not(ends-with(./#num,'.'))">
<a name="{translate(./#num,'.','-')}"/>
<span class="phrase">
<xsl:value-of select="./#num"></xsl:value-of>
</span>
</xsl:when>
<xsl:when test="./#num">
<span class="item-num">
<xsl:value-of select="./#num"></xsl:value-of>
</span>
</xsl:when>
</xsl:choose>
<xsl:apply-templates/>
</div>
</li>
</xsl:template>
<xsl:template match="page[not(preceding-sibling::node()[not(self::text()) or normalize-space()])]"/>
here i don't want phrase around other numbers than i format X.X X.X.X.
you can find a demo of this issue here.
please let me know how can i fix it.
Thanks
Perhaps you need to add a check on whether the num attribute contains a full-stop in the first place
<xsl:when test="contains(#num, '.') and not(ends-with(./#num,'.'))">
i.e. #num contains a full-stop, but not one at the end.
I'd suggest you do it this way:
<xsl:template match="item">
<!-- do one thing here -->
</xsl:template>
<xsl:template match="item [not(ends-with(#num, '.'))] [not (translate(#num, '.123456789', ''))]">
<!-- do another thing here -->
</xsl:template>
--
Doing the predicate by regex as suggested by Ian Roberts would probably be more elegant.

How to match this OR that in an xsl template?

In my Sharepoint fldtypes_custom.xsl file, I have this code, which works perfectly. However, I want to use the same code on three or four similar fields.
Is there a way I can match fields named status1 OR status2, OR status3 in the same template? Right now I have to have three copies of this block of code where the only difference is the fieldref name. I would like to consolodate the code.
<xsl:template match="FieldRef[#Name='status1']" mode="body">
<xsl:param name="thisNode" select="."/>
<xsl:variable name="currentValue" select="$thisNode/#status1" />
<xsl:variable name="statusRating1">(1)</xsl:variable>
<xsl:variable name="statusRating2">(2)</xsl:variable>
<xsl:variable name="statusRating3">(3)</xsl:variable>
<xsl:choose>
<xsl:when test="contains($currentValue, $statusRating1)">
<span class="statusRatingX statusRating1"></span>
</xsl:when>
<xsl:when test="contains($currentValue, $statusRating2)">
<span class="statusRatingX statusRating2"></span>
</xsl:when>
<xsl:when test="contains($currentValue, $statusRating3)">
<span class="statusRatingX statusRating3"></span>
</xsl:when>
<xsl:otherwise>
<span class="statusRatingN"></span>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Is there a way I can match fields named status1 OR status2, OR status3
in the same template?
Use:
<xsl:template match="status1 | status2 | status3">
<!-- Your processing here -->
</xsl:template>
However, I see from the provided code, that the strings "status1", "status2" and "status3" aren't element names -- they are just possible values of the Name attribute of the FieldRef element.
In this case, your template could be:
<xsl:template match="FieldRef
[#Name = 'status1' or #Name = 'status2' or #Name = 'status3']">
<!-- Your processing here -->
</xsl:template>
In case there are many possible values for the Name attribute, one can use the following abbreviation:
<xsl:template match="FieldRef
[contains('|status1|status2|staus3|', concat('|',#Name, '|'))]">
<!-- Your processing here -->
</xsl:template>

Detecting space or text between node and its following-sibling using XSLT

I have an XML document which contains the following example extract:
<p>
Some text <GlossaryTermRef href="123">term 1</GlossaryTermRef><GlossaryTermRef href="345">term 2</GlossaryTermRef>.
</p>
I am using XSLT to transform this to XHTML using the following template:
<xsl:template match="GlossaryTermRef">
<a href="#{#href}" class="glossary">
<xsl:apply-templates select="node()|text()"/>
</a>
</xsl:template>
This works quite well, however I need to insert a space between the two GlossaryTermRef elements if they appear next to each other?
Is there a way to detect whether there is either space or text between the current node and the following-sibling? I can't always insert a space GlossaryTermRef item, as it may be followed by a punctuation mark.
I managed to solve this myself my modifying the template as follows:
<xsl:template match="GlossaryTermRef">
<a href="#{#href}" class="glossary">
<xsl:apply-templates select="node()|text()"/>
</a>
<xsl:if test="following-sibling::node()[1][self::GlossaryTermRef]">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:template>
Can anyone suggest a better way, or see any problems with this solution?
Firstly, "node()|text()" is a longwinded equivalent of "node()". Perhaps you meant "*|node()" which would select the element and text children but not the comments or PIs.
Your solution is probably as good as any. Another would be to use grouping:
<xsl:for-each-group select="node()" group-adjacent="boolean(self::GlossaryTermRef)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:for-each select="current-group()">
<xsl:if test="position() gt 1"><xsl:text> </xsl:text></xsl:if>
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
Naah, that's not pretty at all.
My next attempt would be to use sibling recursion (where the parent does apply-templates on the first child, and each child does apply-templates on the immediately following sibling), but I don't think that's going to be an improvement either.
What about this one? what do you feel?
<xsl:template match="GlossaryTermRef">
<a href="#{#href}" class="glossary">
<xsl:apply-templates select="node()|text()"/>
</a>
</xsl:template>

XSLT xsl:apply-templates Conditional Syntax

I've got the following XSLT code which lists out the folders and their file items from a specified node.
This all works fine but I'd like to parameterise the page and optionally filter its output by a tag value.
Being an XLST numpty I'm stumped with the syntax for the conditional I should be putting in under the <xsl:when test="$tag"> clause - can someone please help ?
<xsl:variable name="tag" select="umbraco.library:Request('tag')" />
<xsl:template match="/">
<!-- Root folder in Media that holds the folders to output -->
<xsl:variable name="mediaRootFolderId" select="5948" />
<!-- Pass in true() to get XML for all nodes below -->
<xsl:variable name="mediaRootNode" select="umbraco.library:GetMedia($mediaRootFolderId, true())" />
<xsl:choose>
<xsl:when test="$tag">
</xsl:when>
<xsl:otherwise>
<!-- If we didn't get an error, output Folder elements that contain Image elements -->
<xsl:apply-templates select="$mediaRootNode[not(error)]/Folder[File]" >
<xsl:sort select="#nodeName"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Template for folders -->
<xsl:template match="Folder">
<div class="folder">
<h2>Folder: <xsl:value-of select="#nodeName" /></h2>
<div class="images">
<xsl:apply-templates select="File">
<xsl:sort select="#nodeName"/>
</xsl:apply-templates>
</div>
</div>
</xsl:template>
<!-- Template for files -->
<xsl:template match="File">
File: <a href="{umbracoFile}" alt="{#nodeName}" ><xsl:value-of select="#nodeName" /></a> <br/>
</xsl:template>
Instead of the long <xsl:choose> instruction, use:
<xsl:apply-templates select=
"$mediaRootNode[not($tag)][not(error)]
/Folder[File]" >
Explanation: For the XPath expression in the select attribute above to select a non-empty set of nodes it is necessary that boolean($tag) is true(). Thus the above single <xsl:apply-templates> instruction is equivalent to the long <xsl:choose> in the question.
you can test if $tag is set like this.
<xsl:param name="tag">
<xsl:message terminate="yes">
$tag has not been set
</xsl:message>
</xsl:param>
This isn't standard though, it'll work on most XSLT processors though.
If you wanted to be absolutely save, you could set the value to an illegal value (such as 1 div 0) and test for it in the body of the template:
<xsl:param name="tag" select="1 div 0" />
<xsl:if test="$tag = 1 div 0">
<xsl:message terminate="yes">
$tag has not been set, or has been set to Infinity, which is invalid.
</xsl:message>
</xsl:if>
Source: O'Reilly XSLT Cookbook