Select specific part of a attribute and assign it to a variable - xslt

I want to select specific part of a attribute and assign it to a variable.
Input :
<p anepane="swedd" conref="../../go_ref/Disti_didin#Disti_didin/k_irl_oi_2222"/>
There are lot of <p> in input. In that all <p> last part starts with p_frm. So I want common template to select p_frm_l1_los_246 from #conref and assign to to a variable.
Tried code :
<xsl:template match="p[#conref]">
<xsl:variable name="aaa" select="self:p"/>
</xsl:template>
Above tried code is not working. Help me to solve this.

Try this approach:
<xsl:template match="p[substring-after(substring-after(#conref,'#'),'/') = 'p_frm_l1_los_246']">
<xsl:variable name="aaa" select="#conref"/>
</xsl:template>
It selects the whole value of p/#conref which ends with p_frm_l1_los_246.
../../page_references/where_used_breadcrumbs.dita#where_used_breadcrumbs/p_frm_l1_los_246

Related

xslt variable set based on condition

I would like to set an xsl variable based on a condition. The xsl below is not currently working for me. I've been able to make two different matchers (<xsl:template match="rule/condition" and <xsl:template match="condition/condition") which enables me to put the ;display:none on just condition/condition matches but that results in the template being duplicated except for the one part of ;display:none. Guess I'm under the impression that I should be able to dynamically set a variable based on a condition but maybe my impression is wrong?
<xsl:template match="condition">
<xsl:variable name="display">
<xsl:if test='name(..)=condition'>;display=none</xsl:if>
</xsl:variable>
<div style="{$divIndent}{$display}">
<a href="javascript:void(0)" style="{$expandPosition}" onclick="expandContents(this)">
<span class="glyphicon glyphicon-plus"></span>
</a>
<condition type="<xsl:value-of select="#type"/>"><br />
<xsl:apply-templates select="expression" />
<xsl:apply-templates select="condition" />
</condition>
</div>
</xsl:template>
<xsl:if test='name(..)=condition'>;display=none</xsl:if>
This asks if the name of the parent is equal to the value of the child whose name is condition. You probably want to know if the name of the parent is the literal value "condition":
<xsl:if test='name(..)="condition"'>;display=none</xsl:if>
However, it might be more idiomatic to write:
<xsl:if test='parent::condition'>;display=none</xsl:if>
Note also that display:none is valid css, display=none prbably won't work.
Try below code by adding attribute style
<div><xsl:attribute name ="style"><xsl:if test='name(..)=condition'>display=none;</xsl:if></xsl:attribute></div>

How to make XSLT xsl:if work

I am trying to make this (xml 1.0) code work . I am new to this and already exhausted myself in
trying different ways. Does someone know my mistake?
<xsl:for-each select="News/Sport">
<xsl:if test="local-name()='Basketball'">
<p>
<xsl:text>Basketball Sport</xsl:text>
</p>
<xsl:value-of select="News/Sport/Basketball/Phrases/Phrase"/>
</xsl:if>
</xsl:for-each>
When I transform it into an HTML file the content doesn't show up. When I remove the xsl:for each and the xsl:if statements the content is successfully presented. I only wish that the content is first checked (if it is available in the XML file) and if yes, that it is taken from the XML content.
Thank you in advance for your help!
EDIT:
This is my XML code
<News>
<Sport>
<Basketball>
<Phrases>
<Phrase>Zach Randolph recovered the opening tipoff in Game 1 of the Western Conference Finals, and he didn’t touch the ball again until the possession following the Grizzlies’ first timeout.
</Phrase>
<Phrases>
</Basketball>
</Sport>
</News>
EDIT2:
Could you tell me why I cannot apply a template inside this below function? Only the text works now:(
<xsl:for-each select="News/Sport[Basketball]">
<xsl:apply-templates select="News/Sport/*" />
</xsl:for-each>
<xsl:template match="Basketball">
<p>
<xsl:text>Basketball Sport</xsl:text>
</p>
<xsl:apply-templates select="Phrases/Phrase"/>
</xsl:template>
<xsl:for-each select="News/Sport">
<xsl:if test="local-name()='Basketball'">
In this if test, the context node is a Sport element, so local-name() will always be Sport and will never equal Basketball.
I only wish that the content is first checked (if it is available in the XML file) and if yes, that it is taken from the XML content.
The usual way to handle this sort of thing in XSLT is to define templates matching the various nodes that might be present and then applying templates to all the nodes that are actually found. If there are no nodes of a particular type then the corresponding template will not fire
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" />
<xsl:template match="/">
<html>
<body>
<!-- apply templates that match all elements inside Sport, which may
be Basketball, Football, etc. -->
<xsl:apply-templates select="News/Sport/*" />
</body>
</html>
</xsl:template>
<!-- when we find a Basketball element ... -->
<xsl:template match="Basketball">
<p>
<xsl:text>Basketball Sport</xsl:text>
</p>
<xsl:apply-templates select="Phrases/Phrase"/>
</xsl:template>
<!-- when we find a Football element ... -->
<xsl:template match="Football">
<p>
<xsl:text>Football Sport</xsl:text>
</p>
<!-- whatever you need to do for Football elements -->
</xsl:template>
<xsl:template match="Phrase">
<p><xsl:value-of select="." /></p>
</xsl:template>
</xsl:stylesheet>
That way you don't need any explicit for-each or if, the template matching logic handles it all for you.
You are missing the idea of a context node. Within a xsl:for-each, everything you refer to is about or relative to the selected nodes. So, for instance, within <xsl:for-each select="News/Sport">, the context node is the Sport elements, and a test like <xsl:if test="local-name()='Basketball'"> is always going to be false because the local name is always Sport.
It looks like you want just <xsl:if test="Basketball"> which tests whether there are any Basketball child nodes of the current Sport node.
The same thing applies to <xsl:value-of select="News/Sport/Basketball/Phrases/Phrase"/>. Because everything is relative to the Sport node, XSLT is looking for News/Sport/Basketball/Phrases/Phrase within the Sport element, which never exists.
In addition, you can just put text in literally: there is no need for an xsl:text element here, so your code should look like
<xsl:for-each select="News/Sport">
<xsl:if test="Basketball">
<p>Basketball Sport</p>
<xsl:value-of select="Basketball/Phrases/Phrase"/>
</xsl:if>
</xsl:for-each>
You can refine this further by adding a predicate to the for-each selection, so that it matches only Sport elements with a Basketball child. Like this
<xsl:for-each select="News/Sport[Basketball]">
<p>Basketball Sport</p>
<xsl:value-of select="Basketball/Phrases/Phrase"/>
</xsl:for-each>

How to increment a variable inside an XSLT template?

here is briefly the problem I'm facing:
I have built a custom Content Query webpart, I fill this CQWP with items from a list. What I would like to do is to have a special separator each 3 items.
How to tell the XSLT that the current item is the 3, 6 or 9th one, and therefore, that a separator has to be put ?
what I have thought of would be to do something like that in the itemstyle.xsl:
<xsl:variable name="increment" select="0"/>
<xsl:template>
<xsl:with-variable name="increment" select="$increment+1"/>
<xsl:if increment = multiple de 3>
-put a separator-
</xsl:if>
</xsl:template>
but it appears that global variable cannot be used this way. Therefore my second idea would to get sortof the "row number" of the corresponding item in order to get the same information.
Does anybody have any idea of how I can solve this problem ?
XSLT does not do destructive update. Therefore you cannot assign a value to a variable and then overwrite the variable with a new value later.
In your case, you can use the position() function with modulus arithmetic:
<xsl:for-each select="*">
<!-- normal processing here -->
<xsl:if test="position() mod 3 = 0">
<!-- separator here -->
</xsl:if>
</xsl:for-each>

XSL Output Text

<xsl:for-each select="$all_events[g:active = true()][g:body/g:current = true()]">
<xsl:for-each select="g:body">
<h2 class="normal"><xsl:value-of select="g:sub_title" /></h2>
<xsl:for-each select="g:paragraphs">
<xsl:text><xsl:value-of select="g:paragraph" /></xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
Here is my XSL, take notice to the following line:
<xsl:text><xsl:value-of select="g:paragraph" /></xsl:text>
I tried this because the g:paragraph is coming from a WYSIWYG and it was printing out the <p> </p> tags and whatever else. This process of encapsulating it within xsl:text tags caused an error. What is the proper way to either hide the tags (because I want the styles to still be applied if included (i.e. bold, underlined)?
Edit:
The output currently is <p>whatever</p>
I want it to be whatever
Ah, I figured it out. I just had to use disable-output-escaping="yes"
You want to select the text content from a p element inside the g:paragraph element. You can do that this way:
<xsl:for-each select="g:paragraphs">
<xsl:value-of select="g:paragraph/p" />
</xsl:for-each>

I need to build a string from a list of attributes (not elements) in XSLT

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