New Lines to HTML Line Breaks Using XSLT - html

I have the following XML:
<RichText>
Text
Text
Text
</RichText>
I would like to output the following HTML using XSLT 1.0 (2.0 if I really really have to):
<p>
Text<br/>
Text<br/>
Text
</p>
I've tried using the following XSL which gets close:
<xsl:template match="text()">
<xsl:param name="text" select="."/>
<!-- Because we would rely on $text containing a line break when using
substring-before($text,'
') and the last line might not have a
trailing line break, we append one before doing substring-before(). -->
<xsl:value-of select="substring-before(concat($text,'
'),'
')"/>
<br/>
<xsl:if test="contains($text,'
')">
<xsl:apply-templates select=".">
<xsl:with-param name="text" select="substring-after($text,'
')"/>
</xsl:apply-templates>
</xsl:if>
<xsl:template>
This outputs:
<p><br>
Text<br>
Text<br>
Text<br>
<br></p>

For your XSLT 1.0 solution, I think all you need is some xsl:if tests to test if there is non-white space text before and after the current line you are handling.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" />
<xsl:template match="RichText">
<p><xsl:apply-templates /></p>
</xsl:template>
<xsl:template match="text()">
<xsl:param name="text" select="."/>
<xsl:variable name="startText" select="substring-before(concat($text,'
'),'
')" />
<xsl:variable name="nextText" select="substring-after($text,'
')"/>
<xsl:if test="normalize-space($startText)">
<xsl:value-of select="$startText"/>
<xsl:if test="normalize-space($nextText)">
<br />
</xsl:if>
</xsl:if>
<xsl:if test="contains($text,'
')">
<xsl:apply-templates select=".">
<xsl:with-param name="text" select="$nextText"/>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Try the following script:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="RichText">
<p>
<xsl:variable name="txt" select="tokenize(., '
')"/>
<xsl:variable name="len" select="count($txt)"/>
<xsl:for-each select="subsequence($txt, 2, $len - 2)">
<xsl:value-of select="replace(replace(.,'\s+$',''),'^\s+','')"/>
<xsl:if test="position() < last()">
<xsl:text disable-output-escaping="yes"><br/></xsl:text>
</xsl:if>
<xsl:text>
</xsl:text>
</xsl:for-each>
</p>
</xsl:template>
</xsl:transform>
I used XSLT 2.0, as a version MUCH simpler to write.
It is possible to rewrite it using XSLT 1.0, but you must use equivalent
1.0 solutions for:
tokenization,
subsequence,
and trimming.

Related

XSLT template to replace string with string Including HTML Tags

I have this template to replace once string with another:
<xsl:template name="X">
<xsl:param name="field"/>
<xsl:param name="target"/>
<xsl:param name="change"/>
<xsl:value-of select='replace($field, $target, $change )' />
</xsl:template>
This is how I am calling it:
<xsl:call-template name="X">
<xsl:with-param name="field" select="artist"/>
<xsl:with-param name="target" select="'\\n'"/>
<xsl:with-param name="change" select="'
'"/>
</xsl:call-template>
This works in most cases except for the case demonstrated here.
I am trying to replace the string "\n" with a HTML line break <br> or <br />.
I want an actual new line not the tag visible in the output.
I know that it is matching on the \n but I can not make the line break happen.
I either get a literal <br> in the output or nothing happens.
You will need to change your approach. Your template is using replace() which works with strings. You want to replace with markup. I'd use xsl:analyze-string instead:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="break"><br/></xsl:variable>
<xsl:call-template name="X">
<xsl:with-param name="field" select="artist"/>
<xsl:with-param name="target" select="'\n'"/>
<xsl:with-param name="change" select="$break"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="X">
<xsl:param name="field"/>
<xsl:param name="target"/>
<xsl:param name="change"/>
<!-- <xsl:value-of select='replace($field, $target, $change )' />-->
<xsl:analyze-string select="$field" regex="{$target}">
<xsl:matching-substring>
<xsl:sequence select="$change"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>

XSLT calling template inside of an HTML

If someone would be kind enough to tell me why the following <xsl:call-template name="Log"> won't work?
XML File:
<?xml version="1.0" encoding="UTF-8"?>
<TextMessages>
<Message>[Step]</Message>
<Message>Step ID: 1</Message>
<Message>Description</Message>
</TextMessages>
XSLT File:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection ></h2>
<p>
<xsl:call-template name="Log">
</xsl:call-template>
</p>
</body>
</html>
</xsl:template>
<xsl:template name ="Log">
<xsl:variable name="break"><br/></xsl:variable>
<xsl:for-each select="TextMessages">
<p>
<xsl:value-of select="."/>
<xsl:value-of select="$break"/>
</p>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The wanted output:
My CD Collection >
[Step]<br/>
Step ID: 1<br/>
Description<br/>
The real problem has also parameters which I call with parameters, but not sure if that is the problem.
<p>
<xsl:call-template name="Log">
<xsl:with-param name="testId" select="#testId" />
</xsl:call-template>
</p>
<xsl:template name ="Log">
<xsl:param name="testId" />
<xsl:variable name="break"><br/></xsl:variable>
<xsl:for-each select="/t:TestRun/t:Results/t:UnitTestResult[#testId=$testId]/t:Output/t:TextMessages/t:Message">
<p>
<xsl:value-of select="."/>
<xsl:value-of select="$break"/>
</p>
</xsl:for-each>
</xsl:template>
If i am guessing correctly, you want to change:
<xsl:template name ="Log">
<xsl:variable name="break"><br/></xsl:variable>
<xsl:for-each select="TextMessages">
<p>
<xsl:value-of select="."/>
<xsl:value-of select="$break"/>
</p>
</xsl:for-each>
</xsl:template>
to:
<xsl:template name="Log">
<xsl:for-each select="TextMessages/Message">
<p>
<xsl:value-of select="."/>
</p>
<hr/>
</xsl:for-each>
</xsl:template>
Note:
You should never have to use a hack like <br/> to output HTML or XML valid markup;
I am not sure why you need to call a named template here, instead of simply including the xsl:for-each in the first template, or just applying templates to the Message elements.
Adding the line
<xsl:output method="text" version="1.0" encoding="UTF-8" />
to your XSLT file directly after the <xsl:stylesheet... > line will you the (partly desired) result of
My CD Collection >
[Step]
Step ID: 1
Description
With "partly" I do refer to the first line - which you explicitly want to be output by including <h2>My CD Collection ></h2> in your XSLT - but not mention in your output text.

XSLT - How to add new tag each X characters

I want to add a new tag <br\> to my XML each 10 characters. (without including inner nodes)
For example if my input is:
<main>
01234567890123
<a>
link
</a>
45678901234567901234
</main>
I expect is to be somthing like:
<main>
0123456789<br\>0123
<a>
link
</a>
456789<br\>012345679<br\>01234
</main>
I wrote this function:
<!-- Add new "HTML Break" Every X chars -->
<xsl:template name="Add-HTML-Break-After-X-Chars">
<xsl:param name="text" />
<xsl:param name="max-length" />
<xsl:choose>
<xsl:when test="string-length($text) > $max-length">
<!-- Show the X length of the text -->
<xsl:copy-of select="substring($text,1,$max-length)" />
<!-- Adding the special tag -->
<br/>
<!-- continue to add the rest of the text -->
<xsl:call-template name="Add-HTML-Break-After-X-Chars">
<xsl:with-param name="text" select="substring($text, $max-length + 1)" />
<xsl:with-param name="max-length" select="$max-length" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- Show the text (length is less then X) -->
<xsl:copy-of select="$text" />
</xsl:otherwise>
</xsl:choose>
The problem is that I'm getting the output without the inner tags:
<main>
0123456789<br\>0123
link
45<br\>6789012345<br\>67901234
</main>
I'm using this code to call the template:
<xsl:call-template name="Add-HTML-Break-After-X-Chars">
<xsl:with-param name="text" select="main" />
<xsl:with-param name="max-length" select="10" />
</xsl:call-template>
I tried also "value-of" instead of "copy-of".
Is there a way I can keep the tags?
If so, how can I keep them and make a "safe" insert of the new tag?
This is difficult to do in XSLT, because it processes each text node individually, with no information being carried over from the preceding text nodes.
Here's a possible approach:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="main/text()" name="wrap">
<xsl:param name="text" select="."/>
<xsl:param name="line-length" select="10"/>
<xsl:param name="carry">
<xsl:variable name="lengths">
<xsl:for-each select="preceding-sibling::text()">
<length>
<xsl:value-of select="string-length()"/>
</length>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="sum(exsl:node-set($lengths)/length) mod $line-length"/>
</xsl:param>
<xsl:value-of select="substring($text, 1, $line-length - $carry)"/>
<br/>
<xsl:if test="$carry + string-length($text) > $line-length">
<!-- recursive call -->
<xsl:call-template name="wrap">
<xsl:with-param name="text" select="substring($text, $line-length - $carry + 1)"/>
<xsl:with-param name="carry" select="0"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When this is applied to the following test input:
XML
<main>A123456789B123<a>link1</a>456789C123456789D12345678<a>link2</a>9E123456789F1234567</main>
the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<main>A123456789<br/>B123<a>link1</a>456789<br/>C123456789<br/>D12345678<a>link2</a>9<br/>E123456789<br/>F1234567</main>

How to sanitize Mysql queries with XSL template?

I have a XSL associate with an XML file. This XSL aim to create Mysql queries but in my XML I had some special characters like apostroph ' which break my queries. Do you know how I can sanitize my XSL template in order to have safe queries?
Example of my XML file
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="fnac.xsl"?>
<products xmlns="http://zanox.com/productdata/exportservice/v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://zanox.com/productdata/exportservice/v1 http://productdata.zanox.com/exportservice/schema/export-1.1.xsd">
<product>
<name>jack o'connor</name>
<program>3467</program>
</product>
</products>
And my XSL file :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:v1="http://zanox.com/productdata/exportservice/v1">
<xsl:output method="text" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="//v1:product"/>
</xsl:template>
<xsl:template match="v1:product">
<xsl:text>insert into fnac (name, program) values(</xsl:text>
<xsl:value-of select="./v1:name"/>
<xsl:text>,'</xsl:text>
<xsl:value-of select="./v1:program"/>
<xsl:text>'); </xsl:text>
</xsl:template>
</xsl:stylesheet>
Thanks for your inputs!
If you want to remove apostrope from your XML file, you should use this xslt:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:v1="http://zanox.com/productdata/exportservice/v1">
<xsl:output method="text" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="//v1:product"/>
</xsl:template>
<xsl:template match="v1:product">
<xsl:text>insert into fnac (name, program) values(</xsl:text>
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="./v1:name"/>
<xsl:with-param name="from">'</xsl:with-param>
<xsl:with-param name="to" select="' '"/>
</xsl:call-template>
<xsl:text>,'</xsl:text>
<xsl:value-of select="./v1:program"/>
<xsl:text>'); </xsl:text>
</xsl:template>
<xsl:template name="replace-string">
<xsl:param name="text"/>
<xsl:param name="from"/>
<xsl:param name="to"/>
<xsl:choose>
<xsl:when test="contains($text, $from)">
<xsl:variable name="before" select="substring-before($text, $from)"/>
<xsl:variable name="after" select="substring-after($text, $from)"/>
<xsl:copy-of select="$before"/>
<xsl:value-of select="$to" disable-output-escaping="yes"/>
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="$after"/>
<xsl:with-param name="from" select="$from"/>
<xsl:with-param name="to" select="$to"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
which would generate output:
insert into fnac (name, program) values(jack o connor,'3467');
If you only like/need to replace the apostrope , this could be done lot easier with translate().
Try this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:v1="http://zanox.com/productdata/exportservice/v1">
<xsl:output method="text" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="//v1:product"/>
</xsl:template>
<xsl:template match="v1:product">
<xsl:text>insert into fnac (name, program) values(</xsl:text>
<xsl:value-of select='translate(./v1:name,"&apos;", " ")'/>
<xsl:text>,'</xsl:text>
<xsl:value-of select="./v1:program"/>
<xsl:text>'); </xsl:text>
</xsl:template>
</xsl:stylesheet>

Replace HTML Quotes with Plain Text in XSLT

We are using XSLT to translate a RIXML file to XML. Our RIXML contains the following text:
<Title><![CDATA[Looking into the future: Reiterate ‘Buy’]]></Title>
8216 is a left quote and 8217 is a right quote.
How would I replace these HTML codes with their plain text representation, or more simply, just a single quote? This is XSLT version 1.0.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vQ2">""</xsl:variable>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select=
"translate(.,'‘’',$vQ2)"/>
</xsl:template>
</xsl:stylesheet>
I came upon this function which worked well for me. I call it to replace all the characters I need to:
<xsl:template name="string-replace-all">
<xsl:param name="text" />
<xsl:param name="replace" />
<xsl:param name="by" />
<xsl:choose>
<xsl:when test="contains($text, $replace)">
<xsl:value-of select="substring-before($text,$replace)" />
<xsl:value-of select="$by" />
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text"
select="substring-after($text,$replace)" />
<xsl:with-param name="replace" select="$replace" />
<xsl:with-param name="by" select="$by" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>