below is my XML which consist below nodes.
<richtext >
<par def="3">
paragraph value 1
<run>
<font style="underline" />run value 1
</run>.
paragraph value 2
<run>
<font style="underline" />run value 2
</run>
paragraph value 3
<run>
<font style="underline" />run value 2 <br /> run value on new line
</run>
paragraph value 4
</par>
</richtext>
i am transforming above xml to html using below xslt. i am new to xslt please help me.
<xsl:template match="/">
<div>
<xsl:apply-templates select="richtext" />
</div>
<xsl:template name="richtext">
<xsl:apply-templates select="par" />
<xsl:apply-templates select="table" />
<xsl:template match="par">
<p>
<xsl:if test="text()">
<xsl:value-of disable-output-escaping="yes" select="text()" />
<xsl:value-of disable-output-escaping="yes" select="*/following-sibling::text()" />
</xsl:if>
<xsl:if test="run">
<xsl:apply-templates select="run" />
<xsl:value-of disable-output-escaping="yes" select="run/following-sibling::text()" />
</xsl:if>
</p>
<xsl:template match="run">
<span>
<xsl:call-template name="style" />
<xsl:value-of disable-output-escaping="yes" select="current()" />
</span>
i want below html output
<p>
paragraph value 1
<span style="underline">
run value 1
</span>.
paragraph value 2
<span style="underline">
run value 2 <br /> run value on new line
</span>
paragraph value 3
<span style="underline">
run value 2
</span>
paragraph value 4
</p>
Please see updated HTML output. can you please tell me what to do ? i want to preserve style from xml.
Edit : if we add in XML then i want text after on new line in HTML.
It looks like you want to do the following:
Convert richtext to div
Convert par to p
Convert run to span
Convert font to an attribute
In which case, a series of simple templates should do it.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="richtext">
<div>
<xsl:apply-templates select="par|table" />
</div>
</xsl:template>
<xsl:template match="par">
<p>
<xsl:apply-templates />
</p>
</xsl:template>
<xsl:template match="run">
<span>
<xsl:call-template name="style" />
<xsl:apply-templates />
</span>
</xsl:template>
<xsl:template match="font" />
<xsl:template name="style">
<xsl:attribute name="style">
<xsl:for-each select="font">
<xsl:if test="position() > 1">; </xsl:if>
<xsl:value-of select="#style" />
</xsl:for-each>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Note the template matching "font" isn't really necessary here. As the font elements in your example have no child, you could omit the template as the built-in templates will just skip over it, and output nothing.
Related
For the below simplified transformation sheet, I'd like to add the following functions:
For "Turnover 2019" and "Turnover 2020" I'd like to get the values below 1mio in a red badge and over 1mio in a green badge. I tried to implement this for "Turnover 2019", however I just get a blank output. The problem seems to be with the <xsl:if test="...">-part, as I do get the correct value if I just enter <xsl:value-of select="key('keyToCreditcard', id)/turnover_2019"/> (see "Turnover 2020").
For "Total Turnover" I need to sum up the values of "Turnover 2019" and "Turnover 2020". How do I do that?
XSL:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="keyToPerson" match="person" use="id"/>
<xsl:key name="keyToCreditcard" match="creditcard" use="cardowner"/>
<xsl:template match="/">
<xsl:for-each select="data/persons/person">
<xsl:if test="turnoverYear1 < 1000000">
<span class="badge bg-warning mx-2"><xsl:value-of select="key('keyToCreditcard', id)/turnoverYear1"/></span>
</xsl:if>
<xsl:if test="turnoverYear1 > 999999.99">
<span class="badge bg-danger mx-2"><xsl:value-of select="key('keyToCreditcard', id)/turnoverYear1"/></span>
</xsl:if>
<xsl:value-of select="key('keyToCreditcard', id)/turnoverYear2"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Input:
<data>
<persons>
<person>
<id>1</id>
<name>Jeramie Bischoff</name>
<city>Luzern</city>
<birthdate>04/16/1951</birthdate>
</person>
</persons>
<creditcards>
<creditcard>
<id>1</id>
<cardtype>Visa</cardtype>
<cardnumber>4041592612048990</cardnumber>
<turnoverYear1>952411.33</turnoverYear1>
<turnoverYear2>6135840.0</turnoverYear2>
<cardowner>1</cardowner>
</creditcard>
</creditcards>
</data>
Expected Output:
Name:Jeramie Bischoff
City: Luzern
birthdate: 04/16/1951
Card Type: Visa
Card Number: 4041592612048990
Turnover 2019: 952411.33
Turnover 2020: 6135840.0
Total Turnover: 7088251.33
Based on your sample XML, a good approach would be to split things up into multiple templates.
One to handle creating the basic HTML document structure (match="/"), one to handle <person> elements, one to handle the turnover badges, and so on. In general it's beneficial to prefer <xsl:apply-templates> over cramming everything into a single template with a bunch of nested <xsl:for-each>.
Since you have an <xsl:key> that links person IDs to credit cards, use that to fetch the <creditcard> into a variable ($cc) and work with it.
<xsl:output method="html" indent="yes" />
<xsl:key name="keyToCreditcard" match="creditcard" use="cardowner"/>
<xsl:template match="/">
<html>
<!-- ... -->
<xsl:apply-templates select="data/persons/person" />
<!-- ... -->
</html>
</xsl:template>
<xsl:template match="person">
<div class="person">
<xsl:variable name="cc" select="key('keyToCreditcard', id)" />
<!-- your question #1 -->
<div>
<xsl:text>Turnover 2019: </xsl:text>
<xsl:apply-templates select="$cc/turnoverYear1" />
</div>
<div>
<xsl:text>Turnover 2020: </xsl:text>
<xsl:apply-templates select="$cc/turnoverYear2" />
</div>
<!-- your question #2 -->
<div>
<xsl:text>Total Turnover: </xsl:text>
<xsl:value-of select="$cc/turnoverYear1 + $cc/turnoverYear2" />
</div>
</div>
</xsl:template>
<xsl:template match="turnoverYear1|turnoverYear2">
<span>
<xsl:attribute name="class">
<xsl:text>badge mx-2 </xsl:text>
<xsl:choose>
<xsl:when test=". < 1000000">bg-warning</xsl:when>
<xsl:when test=". < 99999.99">bg-danger</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:value-of select="." />
</span>
</xsl:template>
which results in
<html>
<div class="person">
<div>Turnover 2019: <span class="badge mx-2 bg-warning">952411.33</span></div>
<div>Turnover 2020: <span class="badge mx-2">6135840.0</span></div>
<div>Total Turnover: 7088251.33</div>
</div>
</html>
Because the year numbers and XML element names are a moving target in your XML, you would have to keep adjusting the XSLT code every year. That's completely unnecessary. It's a lot smarter to keep the year out of the XML element names. Move it into an attribute:
<creditcards>
<creditcard>
<id>1</id>
<cardtype>Visa</cardtype>
<cardnumber>4041592612048990</cardnumber>
<turnover year="2019">952411.33</turnover>
<turnover year="2020">6135840.0</turnover>
<cardowner>1</cardowner>
</creditcard>
</creditcards>
With a <creditcard> setup like this, the XSLT code gets more generic.
<xsl:template match="person">
<div class="person">
<xsl:variable name="cc" select="key('keyToCreditcard', id)" />
<!-- your question #1 -->
<xsl:apply-templates select="$cc/turnover" />
<!-- your question #2 -->
<div>
<xsl:text>Total Turnover: </xsl:text>
<xsl:value-of select="sum($cc/turnover)" />
</div>
</div>
</xsl:template>
<xsl:template match="turnover">
<div>
<xsl:value-of select="concat('Turnover ', #year, ': ')" />
<span>
<xsl:attribute name="class">
<xsl:text>badge mx-2 </xsl:text>
<xsl:choose>
<xsl:when test=". < 1000000">bg-warning</xsl:when>
<xsl:when test=". < 99999.99">bg-danger</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:value-of select="." />
</span>
</div>
</xsl:template>
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.
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.
I am trying to debug some xsl code that puts links gathered from an XML Document with their descriptions in a list, it seems to be working, except when handling multiple links gathered from a single string that are delimited with a "|" and "^" symbol.
The XML Line that contains this code is very similar to this:
<WebContent>
<content_type_desc>Links</content_type_desc>
<content_value>Google|http://www.google.com^Yahoo|http://www.yahoo.com^Bing|Http://www.bing.com</content_value>
</WebContent>
The xsl that handles this is like
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="html" indent="yes" encoding="utf-8"/>
<xsl:template match="/">
<!--Links-->
<xsl:variable name="links" select="//WebContent[content_type_desc='Links']/content_value "/>
<!--Links-->
<!--Links section-->
<xsl:if test="$links != ''">
<br />
<xsl:choose>
<xsl:when test="contains($links,'^')">
<xsl:variable name="URL" select="substring-after(substring-before($links,'^'),'|')"/>
<xsl:variable name="URLText" select="substring-before($links,'|')"/>
<strong>Links: </strong><br />
<xsl:value-of select="$URLText"/>
<br />
</xsl:when>
<xsl:otherwise>
<xsl:variable name="URL" select="substring-after($links,'|')"/>
<xsl:variable name="URLText" select="substring-before($links,'|')"/>
<strong>Links: </strong><br />
<xsl:value-of select="$URLText"/>
<br />
</xsl:otherwise>
</xsl:choose>
<br />
</xsl:if>
<!--Links section-->
</xsl:template>
</xsl:stylesheet>
What's wrong with this is that it only outputs
<a href="http://www.google.com>Google</a>
When I would like it to output
<a href="http://www.google.com>Google</a>
<a href="http://www.yahoo.com>Yahoo</a>
<a href="http://www.bing.com>Bing</a>
Any help will be greatly appreciated as this has me completely stumped.
Assuming you are actually only using XSLT 1.0, to solve this you could make use of a named-template which is called recursively.
At the moment, the code is only processing the URL before the first ^ in the code. What you could do is put the main bulk of the code that checks the links variable in a named template. Then, in the xsl:when condition that runs when the URL does contain a ^ you add a recursive call to the named template, but passing in only the substring after the ^.
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="html" indent="yes" encoding="utf-8"/>
<xsl:template match="/">
<xsl:param name="links" select="//WebContent[content_type_desc='Links']/content_value "/>
<xsl:if test="$links != ''">
<strong>Links: </strong>
<br />
<xsl:call-template name="splitlinks">
<xsl:with-param name="links" select="$links" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="splitlinks">
<xsl:param name="links" />
<xsl:choose>
<xsl:when test="contains($links,'^')">
<xsl:variable name="URL" select="substring-after(substring-before($links,'^'),'|')"/>
<xsl:variable name="URLText" select="substring-before($links,'|')"/>
<xsl:value-of select="$URLText"/>
<br />
<xsl:call-template name="splitlinks">
<xsl:with-param name="links" select="substring-after($links,'^')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="URL" select="substring-after($links,'|')"/>
<xsl:variable name="URLText" select="substring-before($links,'|')"/>
<xsl:value-of select="$URLText"/>
<br />
</xsl:otherwise>
</xsl:choose>
<br />
</xsl:template>
</xsl:stylesheet>
Given that you have tagged your question as classic ASP your XSLT processor is likely some version of MSXML for which there is an implementation of http://www.exslt.org/str/functions/tokenize/index.html so you could use that.
How to complete the following style sheet that should produce HTML output in figure 1.
I have styled some part of it but could not make it exactly the same as in figure 1. I have tried "copy element" in XSL but gave me duplicate results.
This is my XML:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="research.xsl"?>
<ResearchGroups xmlns="http://www.sam.com/sam.html">
<Group name="Intelligent Systems Group" id="ISG"> The Intelligent
Systems Group pursues internationally-leading research in a wide
range of intelligent systems. </Group>
<Group name="Robotics" id="RBT"> The Essex robotics group is one of
the largest mobile robotics groups in the UK. </Group>
<Staff name="Callaghan, Vic" title="Professor" groups="ISG RBT">
Intelligent environments and robotics. </Staff>
<Staff name="Gu, Dongbing" title="Dr" groups="RBT"> Multi-agent
and distributed control systems. </Staff>
</ResearchGroups>
My XSLT:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rg="http://www.sam.com/sam.html"
xmlns="http://wwww.w3.org/1999/xhtml"
version="2.0"> <xsl:template match="/">
<html>
<head> <title>Research Groups</title> </head>
<body> <xsl:apply-templates select="//rg:Group"/> </body>
</html> </xsl:template> <xsl:template match="rg:Group">
<xsl:variable name="ID" select="#id"/>
<h3> <a name="{$ID}"> <xsl:value-of select="#name"/> </a> </h3>
<p> <xsl:value-of select="text()"/> </p>
</xsl:template>
</xsl:stylesheet>
HTML Figure 1
the XSLT style sheet should output the following HTML
Something like this I think:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rg="http://www.sam.com/sam.html" xmlns="http://wwww.w3.org/1999/xhtml" version="2.0">
<xsl:template match="/">
<html>
<head>
<title>Research Groups</title>
</head>
<body>
<xsl:apply-templates select="//rg:Group"/>
</body>
</html>
</xsl:template>
<xsl:template match="rg:Group">
<xsl:variable name="ID" select="#id"/>
<h3>
<a name="{$ID}">
<xsl:value-of select="#name"/>
</a>
</h3>
<p>
<xsl:value-of select="text()"/>
</p>
<ul>
<xsl:apply-templates select="//rg:Staff[contains(#groups, current()/#id)]">
<xsl:with-param name="curGroup"><xsl:value-of select="#id"/></xsl:with-param>
</xsl:apply-templates>
</ul>
</xsl:template>
<xsl:template match="rg:Staff">
<xsl:param name="curGroup"/>
<li>
<xsl:value-of select="#name"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="text()"/>
<xsl:if test="//rg:Group[(#id != $curGroup) and contains(current()/#groups, #id)]">
<xsl:text> ( </xsl:text>
<xsl:apply-templates select="//rg:Group[(#id != $curGroup) and contains(current()/#groups, #id)]" mode="otherGroups"/>
<xsl:text> ) </xsl:text>
</xsl:if>
</li>
</xsl:template>
<xsl:template match="rg:Group" mode="otherGroups">
<a href="#{#id}">
<xsl:value-of select="#id"/>
</a>
</xsl:template>
</xsl:stylesheet>