I've got a string that could have line breaks and apostrophes. I need to do a replace for both. I have the following xsl code:
<xsl:call-template name="replaceapostrophes">
<xsl:with-param name="string">
<xsl:call-template name="replacelinefeeds">
<xsl:with-param name="string" select="hl7:text/hl7:paragraph"/>
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
<!-- template for replacing line feeds with <br> tags for page display -->
<xsl:template name="replacelinefeeds">
<xsl:param name="string"/>
<xsl:choose>
<xsl:when test="contains($string,'
')">
<xsl:value-of select="substring-before($string,'
')"/>
<br/>
<xsl:call-template name="replacelinefeeds">
<xsl:with-param name="string" select="substring-after($string,'
')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- template for replacing html encoded apostrophes for page display -->
<xsl:template name="replaceapostrophes">
<xsl:param name="string"/>
<xsl:choose>
<xsl:when test="contains($string, ''')">
<xsl:value-of select="substring-before($string,''')"/>'<xsl:call-template name="replaceapostrophes">
<xsl:with-param name="string" select="substring-after($string,''')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This is the xml code:
<text>
<paragraph>Adding apostrophe to the patient's instructions
and checking for a second line</paragraph>
</text>
However, when this runs, I'm getting the apostrophe accounted for, but not the line breaks.
Adding apostrophe to the patient's instructions and checking for a second line
rather than
Adding apostrophe to the patient's instructions
and checking for a second line
It does work fine if there's one or the other but not both in the same string.
Is there a different way I need to do these?
Thanks
Try doing it the other way around (replace apostrophes first, then line breaks).
Basically you are putting an HTML <br/> element into your variable, and then taking its text value to replace the apostrophes , which removes the line break again.
Use the templates the other way round, i.e. first replace the apostrophes, then the line feeds. And make sure for the result of the replacement of line feeds you use xsl:copy-of and not xsl:value-of when you output it, as otherwise the br elements will be lost. So in case you have <xsl:variable name="text"><xsl:call-template name="replacelinefeeds">..</xsl:call-template></xsl:variable>, make sure you use <xsl:copy-of select="$text"/>.
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>
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...
In one of my xml message I have a particular tag in which some (unknown) of last positions are filled with a particular character(/). But during XSLT I want to remove this character and generate another string free from that characters in last positions.
Your question is very vague, but you would create an identity transform that copied every element recursively, with a special template for the elements you want to modify, like this
<xsl:template match="particular-tag">
<xsl:copy>
<xsl:value-of select="substring-before(., '/')"/>
</xsl:copy>
</xsl:template>
This will remove all characters from the first slash onwards from every <particular-tag> element.
If I understand the question correctly, you have a string which may end with zero or more slashes; you want a function or a template to strip the trailing slashes -- but not other slashes -- from the string. In XSLT 2.0, the simplest thing to do is write a function to do this. There are many ways to write the function; a straightforward one is this one (which generalizes the problem to stripping any given character off the end of a string, not just slash):
<xsl:function name="my:strip-trailing-char">
<xsl:param name="s" required="yes"/>
<xsl:param name="c" required="yes"/>
<xsl:choose>
<xsl:when test="ends-with($s,$c)">
<xsl:value-of select="my:strip-trailing-char(
substring($s,1,string-length($s) - 1),
$c
)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$s"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
In XSLT 1.0, you can do the same thing with a named template:
<xsl:template name="my:strip-trailing-char">
<xsl:param name="s" required="yes"/>
<xsl:param name="c" required="yes"/>
<xsl:choose>
<xsl:when test="ends-with($s,$c)">
<xsl:call-template name="my:strip-trailing-char">
<xsl:with-param name="s"
select="substring($s,1,string-length($s) - 1)"/>
<xsl:with-param name="c" select="$c"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$s"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I an trying to transform some data. The data contained in my xml field "Source/End/Edgecode" looks like -000016
I want to remove the leading dash. I thought I could use abs or right, but I don't know the correct syntax.
Thanks for any help...
* FROM CLIP NAME:
<xsl:template name="frame_to_tc">
<xsl:param name="frame"/>
<xsl:variable name="hh" select="($frame div $fph)"/>
<xsl:variable name="rh" select="($frame mod $fph)"/>
<xsl:variable name="mm" select="($rh div $fpm)"/>
<xsl:variable name="rm" select="($rh mod $fpm)"/>
<xsl:variable name="ss" select="($rm div $fps)"/>
<xsl:variable name="rs" select="($rm mod $fps)"/>
<xsl:variable name="ff" select="($rs mod $fps)"/>
<xsl:value-of select="format-number(floor($hh),'00')"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="format-number(floor($mm),'00')"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="format-number(floor($ss),'00')"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="format-number(floor($ff),'00')"/>
<xsl:template name="abs">
<xsl:param name="input" select="0">
<xsl:variable name="num" select="number(input)" />
<xsl:choose>
<xsl:when test="$num >= 0">
<xsl:value-of select="$num" />
</xsl:when>
<xsl:when test="$num < 0">
<xsl:value-of select="-1 * $num" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$input" />
</xsl:otherwise>
</xsl:choose>
<xsl:template>
callable like this:
<xsl:variable name="absnum">
<xsl:call-template name="abs">
<xsl:with-param name="input" select="some/number" />
</xsl:call-template>
</xsl:variable>
You can use a simple substring.
<xsl:value-of select="substring($yourVariable, 2)"/>
<!-- In XSLT, the index starts from 1, so here you are taking a substring starting from the second character, in essence, leaving out the hyphen-->
Or even the node directly instead of the variable..
<xsl:value-of select="substring(/Your/Path/Here, 2)"/>
Since you are not specifying a specific number of characters to return, it will return the rest of the string completely. You can restrict the number of characters returned which is done by adding a comma and then specifying a second length upto which to cut) Ex: substring(/Your/Path/Here , 2, 5)
This will cut from the 2nd character upto five characters, If the string is "1234567" it will return "23456".
NOTE: This is assuming that you only want to remove the leading hyphen. If you want to remove the leading zeroes as well, a number() typecast should do the trick.
to remove the leading dash, try this XPath function:
<xsl:value-of select="substring-after($x,'-')"/>
I'm a complete XSL novice, writing an XSL file to format lots of different error messages that can appear in the output log created by an application into CSV format.
Slight variations might occur in the matchable tags in these output logs. For example, one sentence in the log might contain the phrase "Service Month/Year:" but another one, from a different area of the application, would contain "Svc Month/Yr:" instead.
Is there a way to put both those variations of that phrase in a single line of my XSL? Or do I have to repeat the entire If block, with each variation in the phrase that I want to match on in its own If block?
I tried posting the XSL here surrounded by backticks, but it runs all together in one big lump that's impossible to read. If anyone can help with this question, I'm happy to post it if you tell me how to make it readable. :-)
Thank you.
XSL allows combining of conditional statements like other langues. Each one doesn't require its on if statement. Were you thinking something along these lines?
<xsl:choose>
<xsl:when test="contains(text(), 'Service Month/Year:')
or contains(text(), 'Svc Month/Yr:')
">
<!-- do something -->
</xsl:when>
</xsl:choose>
Keep in mind xml/xsl is case sensitive. To make it more flexible, it is even more verbose:
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:choose>
<xsl:when test="contains(translate(text(), $upper, $lower), 'service month/year:')
or contains(translate(text(), $upper, $lower), 'svc month/yr:')
">
<!-- do something -->
</xsl:when>
</xsl:choose>
EDIT: And an even better answer I whipped up
<xsl:template name="containsToken">
<xsl:param name="inputString"/>
<xsl:param name="tokens"/>
<xsl:param name="delimiter"/>
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:choose>
<xsl:when test="contains($tokens, $delimiter)">
<xsl:variable name="token">
<xsl:value-of select="substring-before($tokens, $delimiter)"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains(translate($inputString, $upper, $lower), translate($token, $upper, $lower))">
<xsl:text>True</xsl:text>
</xsl:when>
<xsl:otherwise>
<!-- loop -->
<xsl:call-template name="containsToken">
<xsl:with-param name="inputString" select="$inputString"/>
<xsl:with-param name="tokens" select="substring-after($tokens, $delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="contains(translate($inputString, $upper, $lower), translate($tokens, $upper, $lower))">
<xsl:text>True</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>False</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Usage:
<xsl:variable name="found">
<xsl:call-template name="containsToken">
<xsl:with-param name="inputString" select="text()"/>
<xsl:with-param name="tokens" select="'Service Month/Year:|Svc Month/Yr:'"/>
<xsl:with-param name="delimiter" select="'|'"/>
</xsl:call-template>
</xsl:variable>
<xsl:if test="$found = 'True'">
<!-- process -->
</xsl:if>
Delimiter can be whatever character or charcters you want. Tokens is a list of things to search for with the delimiter between each one. Enjoy!