is any way to create function create wanted spaces in the output, because i was keep hot coding were ever i required space <xsl:text>
</xsl:text >, kindly suggest a function to generate a space based on the passed parameter,please advice
You may be looking at recursive template. Something like this:
<xsl:template name="WriteSpaces">
<xsl:param name="count" />
<xsl:if test="$count > 0">
<xsl:text> </xsl:text>
<xsl:call-template name="WriteSpaces">
<xsl:with-param name="count" select="$count - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
Notice that invoking this would actually pollute your code even more than just putting spaces directly. For static content I would probably just use text. For dynamic content (i.e. if you want <xsl:text> </xsl:text><xsl:value-of select="." /><xsl:text> </xsl:text>) I would use concat() function as it does not limit the number of parameters - so you could code above just as <xsl:value-of select="concat(' ', ., ' ')" />.
Related
I am processing an xml file using xslt.
<ns1:declarationStatements>
<ns1:parameterisedEntity>
<ns2:code>NUTSUPSTATE20</ns2:code>
<ns2:localeData>
<ns1:description>
<![CDATA[** When {s} according to instructions {m}g typically weighs {m}g.]]>
</ns1:description>
<ns1:id>20253</ns1:id>
</ns2:localeData>
<ns2:specType>FOOD</ns2:specType>
<ns2:id>6653</ns2:id>
</ns1:parameterisedEntity>
<ns1:textParameters>
<ns1:value>228</ns1:value>
<ns1:id>68225</ns1:id>
<ns1:sequence>2</ns1:sequence>
</ns1:textParameters>
<ns1:textParameters>
<ns1:value>cooked</ns1:value>
<ns1:id>68233</ns1:id>
<ns1:sequence>0</ns1:sequence>
</ns1:textParameters>
<ns1:textParameters>
<ns1:value>255</ns1:value>
<ns1:id>68229</ns1:id>
<ns1:sequence>1</ns1:sequence>
</ns1:textParameters>
<ns1:id>133421</ns1:id>
</ns1:declarationStatements>
I want to get the text inside <ns1:description> i.e.-
**When {s} according to instructions {m}g typically weighs {m}g
But I want {s}, {m} and {m} to be replaced by the values in <ns1:textParameters>/<ns1:value>. It should look like -
**When cooked according to instructions 255g typically weighs 228g.
I tried doing that by using <xsl:value-of select="ns0:declarationStatements"> and the manipulating string but it is becoming very tedious and complex.
The number of such braces may also vary. So do we have anything like List or Array in XSLT?
Is there any other way or trick I can use to solve this problem?
Thanks
Assuming the parameters are meant to be inserted in order of their ns1:sequence value, I would start by defining a key as:
<xsl:key name="text-param" match="ns1:textParameters" use="ns1:sequence" />
then call the following recursive template with ns1:description as the string param:
<xsl:template name="merge-params">
<xsl:param name="string"/>
<xsl:param name="i" select="0"/>
<xsl:choose>
<xsl:when test="contains($string, '{') and contains(substring-after($string, '{'), '}')">
<xsl:value-of select="substring-before($string, '{')" />
<xsl:value-of select="key('text-param', $i)/ns1:value" />
<!-- recursive call -->
<xsl:call-template name="merge-params">
<xsl:with-param name="string" select="substring-after($string, '}')" />
<xsl:with-param name="i" select="$i + 1" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I have a parameterignoreAttributes which is a comma separated list of things to look for. I want to set a variable copyAttrib to be equal to whether any of them are exactly matched by name().
If xsl were a procedural language where variables could be reassigned, I'd use something like this:
<xsl:variable name="copyAttrib" select="true()">
<xsl:for-each select="tokenize($ignoreAttributes,',')">
<xsl:if test="compare(., name()) != 0">
<xsl:variable name="copyAttrib" select="false()"/>
</xsl:if>
</xsl:for-each>
Unfortunately, I can't do that, because xsl is functional (so says this other answer). So variables can only be assigned once.
I think the solution would look something like:
<vsl:variable name="copyAttrib">
<xsl:choose>
<xsl:when>
<xsl:for-each select="tokenize($ignoreAttributes, ',')">
<xsl:if test="compare(., name()) != 0"/>
</xsl:for-each>
<xsl:otherwise>
<xsl:value-of select="false()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Obviously not exactly that (otherwise I wouldn't be asking.)
I know that I could bypass the tokenize and for-each loop by just using replaces on ignoreAttributes and changing all the , to | and then using matches, but I'd like to avoid that if possible because then I need to deal with the possibility that ignoreAttributes (which the user provides) might contain some special characters that will change the regex pattern and escape them all.
I have a parameterignoreAttributes which is a comma separated list of things to look for. I want to set a variable copyAttrib to be equal to whether any of them are exactly matched by name().
That sounds to me like
<xsl:variable name="copyAttrib" as="xs:boolean"
select="tokenize($parameterignoreAttributes, ',') = name()"/>
You say:
Unfortunately, I can't do that, because xsl is functional
when what you mean is: "Fortunately, I don't need to do that, because XSLT is functional".
An XSLT-1.0 way of doing this is by using a recursive, named template:
<xsl:template name="copyAttrib">
<xsl:param name="attribs" />
<xsl:choose>
<xsl:when test="normalize-space(substring-before($attribs,',')) = normalize-space(name(.))">
<xsl:value-of select="'true'" />
</xsl:when>
<xsl:when test="normalize-space($attribs) = ''">
<xsl:value-of select="'false'" />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="copyAttrib">
<xsl:with-param name="attribs" select="substring-after($attribs,',')" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Apply this template onto the current, the selected, node and wrap it in a <xsl:variable>:
<xsl:variable name="copyAttribResult">
<xsl:call-template name="copyAttrib">
<xsl:with-param name="attribs" select="'a,b,c,...commaSeparatedValues...'" />
</xsl:call-template>
</xsl:variable>
to get either true or false as a result.
Is there an easy way to indent whole blocks of text in XSL? I'd like to indent blocks of text to roughly match the nested levels of the XML.
The answer here, XSL: output of "nested" structures
proposes an indent parameter which is increased as you recurse down.
This is a good start, but requires a long <value-of> tag at the beginning of every line, which of course can't be put inside of the <xsl:text> block.
So, is there a way to use that to indent a multi-line <xsl:text> block? I don't want to have to wrap every single line separately into <xsl:text> blocks, just so that I can add a variable to the beginning of each line.
Example template:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="drv">
<xsl:param name="pIndent"/>
<xsl:text>
Column {
anchors { left: parent.left; right: parent.right }
</xsl:text>
<xsl:apply-templates select="snc|kap">
<xsl:with-param name="pIndent" select="concat($pIndent, ' ')"/>
</xsl:apply-templates>
<xsl:text>
}</xsl:text>
</xsl:template>
</xsl:stylesheet>
So, I want to be able to output the text at the appriate indentation level. So, is there a way to dynamically indent the whole <xsl:text> block (or blocks in this case)? This will allow the output code to be indented correctly, making it much easier for me to debug.
Output like this, but indented to the correct level in the surrounding code:
Column {
anchors { left: parent.left; right: parent.right }
[Code from some other template
kept at this indent level]
}
I'll try and summarise the question in a couple of different ways, as people are struggling to understand it:
Given a level of indentation known at runtime, how can you indent the contents of an entire <xsl:text> block to that level?
Or alternatively: Given a value known at runtime, how can you prepend the value to the beginning of every line in an <xsl:text> block?
You essentially want a "function" that can take a string and prepend a particular prefix to the start of the string and also immediately after every newline character that the string contains. In XSLT 2.0 this would be very simple using the regular expression replace function, but unfortunately lxml only supports XSLT 1.0.
In XSLT 1.0 I'd approach this using a tail-recursive named template:
<xsl:template name="printIndented">
<xsl:param name="text" />
<xsl:param name="indent" />
<xsl:if test="$text">
<xsl:value-of select="$indent" />
<xsl:variable name="thisLine" select="substring-before($text, '
')" />
<xsl:choose>
<xsl:when test="$thisLine"><!-- $text contains at least one newline -->
<!-- print this line -->
<xsl:value-of select="concat($thisLine, '
')" />
<!-- and recurse to process the rest -->
<xsl:call-template name="printIndented">
<xsl:with-param name="text" select="substring-after($text, '
')" />
<xsl:with-param name="indent" select="$indent" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
To use the "function" you'd have to do something like
<xsl:call-template name="printIndented">
<xsl:with-param name="indent" select="$pIndent" />
<xsl:with-param name="text"
>Column {
anchors { left: parent.left; right: parent.right }</xsl:with-param>
</xsl:call-template>
<xsl:apply-templates select="snc|kap">
<xsl:with-param name="pIndent" select="concat($pIndent, ' ')"/>
</xsl:apply-templates>
<xsl:call-template name="printIndented">
<xsl:with-param name="indent" select="$pIndent" />
<xsl:with-param name="text">}</xsl:with-param>
</xsl:call-template>
Of course, you then have to be extremely careful not to use an auto-formatter on the XSLT source code itself, as that would throw all your carefully indented code out of the window...
So,
I have an XSLT template which expects a node set as a parameter and uses this as display text. However, sometimes this node is empty in the XML and I want to pass default display text instead of the display text not showing up instead:
Works:
<xsl:call-template name="myTemplate">
<xsl:with-param name="parm1" select="//element">
</xsl:call-template>
Doesn't work:
<xsl:variable name="dispText">
<xsl:choose>
<xsl:when test="string-length(//element) = 0">
<xsl:value-of select="'Default Text'" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//element" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="myTemplate">
<xsl:with-param name="parm1" select="$dispText">
</xsl:call-template>
Any ideas as to how I could accomplish this? I've tried all sorts of things with no luck :(
It seems like all I need to do is create a new node with the display text I want, but I don't know if that is even possible?
Thanks
Implement the default handling in the template, because that's where it belongs. The calling side should be consistent and not have side-effects on the template behavior (i.e. you should not be able to "forget" passing in the default value).
<xsl:template name="myTemplate">
<xsl:param name="parm1" /><!-- node set expected! -->
<!-- actual value or default -->
<xsl:variable name="value1">
<xsl:choose>
<xsl:when test="not($parm1 = '')">
<xsl:value-of select="$parm1" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$default1" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- work with $value1 from this point on -->
</xsl:template>
I'm guessing //element is a nodeset and using string-length() on it might not be valid. Try converting it to a string() first?
Can anyone tell me how to select the URL from this:
<media:content url="http://feedproxy.google.com/~r/TEDTalks_video/~5/aZWq6PY05YE/TimBrown_2009G.mp4" fileSize="57985745" type="video/mp4" />
I want to:
Create a link to this file:
Trim the URL from:
http://feedproxy.google.com/~r/TEDTalks_video/~5/aZWq6PY05YE/TimBrown_2009G.mp4
to:
TimBrown_2009G
and then take: TimBrown_2009G and use it as part of a URL
Selecting the URL. You just need to make sure that you have the correct namespace URI.
<xsl:value-of xmlns:media="http://search.yahoo.com/mrss/"
select="media:content/#url"/>
Trimming down the URL. How to do this best depends on whether you are using XSLT 1 or 2, since the latter has better string handling functions from XPath 2.0.
If you are using XSLT 1, you might want to create a helper template to return the last segment out of a delimited string:
<xsl:template name="last-substring-after">
<xsl:param name="string"/>
<xsl:param name="separator"/>
<xsl:choose>
<xsl:when test="contains($string, $separator)">
<xsl:call-template name="last-substring-after">
<xsl:with-param name="string"
select="substring-after($string, $separator)"/>
<xsl:with-param name="separator"
select="$separator"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Then you can make use of that to extract the last segment of the URL, and proceed to extract the part before the dot. Assuming the URL is in variable url:
<xsl:variable name="name">
<xsl:call-template name="last-substring-after">
<xsl:with-param name="string" select="$url"/>
<xsl:with-param name="separator" select="'/'"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="substring-before($name, '.')"/>