How can I replace HTML content using XSLT? - html

I would like to remove a certain text from an HTML page using XSLT. The text I would like to remove is <h2>OLD TEXT</h2>. I'm trying to remove it by replacing the text with an empty string but I don't get it to work.
When calling my string-replace-all function with "<h2>OLD TEXT</h2>" text as input I get the following error:
The value of the attribute "select" associated with an element type "xsl:with-param" must not contain the '<' character.
When calling my string-replace-all function with just "OLD TEXT" as input the text gets replaced but the output
is no longer HTML, it's just plain text without the HTML tags.
How could I do to replace <h2>OLD TEXT</h2> and still get the output in HTML format?
My code:
<?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" />
<xsl:template name="string-replace-all">
<xsl:param name="text" />
<xsl:param name="replace" />
<xsl:param name="by" />
<xsl:choose>
<xsl:when test="$text = '' or $replace = ''or not($replace)" >
<!-- Prevent this routine from hanging -->
<xsl:copy-of select="$text" />
</xsl:when>
<xsl:when test="contains($text, $replace)">
<xsl:copy-of select="substring-before($text,$replace)" />
<xsl:copy-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:copy-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:variable name="updatedHtml">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="//div[#id='mainContent']" />
<xsl:with-param name="replace" select="'OLD TEXT'" />
<xsl:with-param name="by" select="''" />
</xsl:call-template>
</xsl:variable>
<xsl:template match="//div[#id='mainContent']">
<xsl:copy-of select="$updatedHtml" />
</xsl:template>
</xsl:stylesheet>
HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test page</title>
</head>
<body>
<div id="container">
<div id="mainContent">
<h1>Header one</h1>
<p>Some text</p>
<h2>OLD TEXT</h2>
<p>Some information about the old text</p>
<br>
More text
<h2>Another header</h2>
Some information <strong>with strong</strong> text.
<h2>Another header again</h2>
Some information <strong>with strong</strong> text.
</div>
</div>
</body>
</html>

Related

Why is this transform of XML / CSS / JPG using XSL to HTML not working?

Note sure how to make one of your online test environments for this issue.
Test XSL script:
<?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" version="4.01"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
doctype-public="//W3C//DTD XHTML 1.0 Transitional//EN"/>
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<xsl:attribute name="lang">
<xsl:value-of select="//Settings//LanguageCode"/>
</xsl:attribute>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<link rel="stylesheet" type="text/css">
<xsl:attribute name="href">
<xsl:value-of select="//Settings/Template/#Style"/>
</xsl:attribute>
</link>
<title>
<!--<xsl:value-of select="//Labels/ReportTitleWorksheets"/>-->
Assignment Slips
</title>
<style type="text/css">
#import url('<xsl:text>2020/</xsl:text><xsl:value-of select="//Settings/Template/#Style"/>');
</style>
</head>
<body>
<xsl:for-each select="AssignmentSlips/Page">
<div class="containerPage">
<xsl:if test="#PageBreakBefore=1">
<br style="page-break-before: always;"/>
</xsl:if>
<xsl:for-each select="StudentSlip">
<div class="containerSlip">
<img alt="s89" width="323px" height="429px">
<xsl:attribute name="src">
<xsl:text>2020\</xsl:text>
<xsl:value-of select="//Settings/Template"/>
</xsl:attribute>
</img>
<div class="fieldName">
<xsl:attribute name="dir">
<xsl:value-of select="//Settings/Direction"/>
</xsl:attribute>
<xsl:value-of select="Student"/>
</div>
<div class="fieldAssisant">
<xsl:attribute name="dir">
<xsl:value-of select="//Settings/Direction"/>
</xsl:attribute>
<xsl:value-of select="Assistant"/>
</div>
<div class="fieldDate">
<xsl:attribute name="dir">
<xsl:value-of select="//Settings/Direction"/>
</xsl:attribute>
<xsl:value-of select="Date"/>
</div>
<div class="fieldCounsel">
<xsl:choose>
<xsl:when test="#ItemPosition='1' and Assistant!=''">
<xsl:text>1st: </xsl:text>
</xsl:when>
<xsl:when test="#ItemPosition='2'">
<xsl:text>2nd: </xsl:text>
</xsl:when>
<xsl:when test="#ItemPosition='3'">
<xsl:text>3rd: </xsl:text>
</xsl:when>
</xsl:choose>
<xsl:attribute name="dir">
<xsl:value-of select="//Settings/Direction"/>
</xsl:attribute>
<xsl:value-of select="StudyPoint"/>
</div>
<xsl:choose>
<xsl:when test="Assignment=1">
<div class="checkBibleReading">✓</div>
</xsl:when>
<xsl:when test="Assignment=2">
<div class="checkInitialCall">✓</div>
<div class="fieldInitialCallIndex">
<xsl:choose>
<xsl:when test="#ItemPosition='1'">
<xsl:text>#1</xsl:text>
</xsl:when>
<xsl:when test="#ItemPosition='2'">
<xsl:text>#2</xsl:text>
</xsl:when>
<xsl:when test="#ItemPosition='3'">
<xsl:text>#3</xsl:text>
</xsl:when>
</xsl:choose>
</div>
</xsl:when>
<xsl:when test="Assignment=3">
<div class="checkFirstReturnVisit">✓</div>
<div class="fieldFirstReturnVisitIndex">
<xsl:choose>
<xsl:when test="#ItemPosition='1'">
<xsl:text>#1</xsl:text>
</xsl:when>
<xsl:when test="#ItemPosition='2'">
<xsl:text>#2</xsl:text>
</xsl:when>
<xsl:when test="#ItemPosition='3'">
<xsl:text>#3</xsl:text>
</xsl:when>
</xsl:choose>
</div>
</xsl:when>
<xsl:when test="Assignment=4">
<div class="checkSecondReturnVisit">✓</div>
<div class="fieldSecondReturnVisitIndex">
<xsl:choose>
<xsl:when test="#ItemPosition='1'">
<xsl:text>#1</xsl:text>
</xsl:when>
<xsl:when test="#ItemPosition='2'">
<xsl:text>#2</xsl:text>
</xsl:when>
<xsl:when test="#ItemPosition='3'">
<xsl:text>#3</xsl:text>
</xsl:when>
</xsl:choose>
</div>
</xsl:when>
<xsl:when test="Assignment=5">
<div class="checkThirdReturnVisit">✓</div>
</xsl:when>
<xsl:when test="Assignment=6">
<div class="checkBibleStudy">✓</div>
</xsl:when>
<xsl:when test="Assignment=7">
<div class="checkTalk">✓</div>
</xsl:when>
<xsl:when test="Assignment=0">
<div class="checkOther">✓</div>
<div class="fieldOther">
<xsl:attribute name="dir">
<xsl:value-of select="//Settings/Direction"/>
</xsl:attribute>
<xsl:value-of select="Other"/>
</div>
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="Location=1">
<div class="checkMainHall">✓</div>
</xsl:when>
<xsl:when test="Location=2">
<div class="checkAuxClass1">✓</div>
</xsl:when>
<xsl:when test="Location=3">
<div class="checkAuxClass2">✓</div>
</xsl:when>
</xsl:choose>
</div>
</xsl:for-each>
</div>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Test XML data:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="AssignmentSlips-2020-v1.xsl"?>
<AssignmentSlips Version="230800">
<Settings>
<LanguageCode>zu</LanguageCode>
<Template Style="s-89-zul.css">s-89-zul.jpg</Template>
<Direction>ltr</Direction>
<ForeignGroupMode>0</ForeignGroupMode>
</Settings>
<Page PageBreakBefore="0">
<StudentSlip ItemPosition="0">
<Student></Student>
<StudyPoint></StudyPoint>
<Material></Material>
<Assignment ItemCount="1">1</Assignment>
<Location>1</Location>
<Date>ULwesine 9 Februwari 2023</Date>
</StudentSlip>
<StudentSlip ItemPosition="1">
<Student></Student>
<StudyPoint></StudyPoint>
<Assignment ItemCount="1">2</Assignment>
<Location>1</Location>
<Date>ULwesine 9 Februwari 2023</Date>
</StudentSlip>
<StudentSlip ItemPosition="2">
<Student></Student>
<StudyPoint></StudyPoint>
<Assignment ItemCount="1">3</Assignment>
<Location>1</Location>
<Date>ULwesine 9 Februwari 2023</Date>
</StudentSlip>
<StudentSlip ItemPosition="3">
<Student></Student>
<StudyPoint></StudyPoint>
<Material></Material>
<Assignment ItemCount="1">6</Assignment>
<Location>1</Location>
<Date>ULwesine 9 Februwari 2023</Date>
</StudentSlip>
</Page>
</AssignmentSlips>
The referenced CSS / JPG files are in a 2020 sub folder:
https://www.dropbox.com/s/vbbqr10gbgmil8s/2020.zip?dl=0
When I open the XML file with Microsoft Edge the display is blank.
When I did manage to view the source it was cropped:
<!DOCTYPE html PUBLIC "//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="zu" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<link rel="stylesheet" type="text/css" href="s-89-zul.css" />
<title>
Assignment Slips
</title>
<style type="text/css">
#import url('2020/s-89-zul.css');
</style>
</head>
<body>
<div class="containerPage">
<div class="containerSlip">
<img alt="s89" width="323px" height="429px" src="2020\s-89-zul.jpg" />
<div class="fieldName" dir="ltr">
</div>
<div class="fieldAssisant" dir="ltr">
</div>
<div class="fieldDate" dir="ltr">ULwesine 9 Februwari 2023</div>
<div class="fieldCounsel" dir="ltr">
</div>
<div class="checkBibleReading">✓</div>
<div class="checkMainHall">✓</div>
</div>
<div class="containerSlip">
<img alt="s89" width="323px" height="429px" src="2020\s-89-zul.jpg" />
<div class="fieldName" dir="ltr">
</div>
<div class="fieldAssisant" dir="ltr">
</div>
<div class="fieldDate" dir="ltr">ULwesine 9 Februwari 2023</div>
<div class="fieldCounsel" dir="ltr">
</div>
<div class="checkInitialCall">✓</div>
<div class="fieldInitialCallIndex">#1</div>
<div class="checkMainHall">✓</div>
</div>
<div class="containerSlip">
<img alt="s89" width="323px" height="429px" src="2020\s-89-zul.jpg" />
<div class="fieldName" dir="ltr">
</div>
<div class="fieldAssisant" dir="ltr">
</div>
<div class="fieldDate" dir="ltr">ULwesine 9 Februwari 2023</div>
<div class="fieldCounsel">2nd:
What have I done wrong here?
Your first problem is that you are testing this code in an environment that gives very poor diagnostics. Ideally, test your code in an XSLT development tool such as Oxygen. If you can only test it in the browser, use a browser like Chrome with a decent developer's console.
When I run it in XSLTfiddle (https://www.xsltfiddle.liberty-development.net) I get a clear error message:
Error executing XSLT at line 70 : An attribute node (dir) cannot be
created after a child of the containing element. Most recent element
start tag was output at line 58 of module
If you look at the surrounding code:
<div class="fieldCounsel">
<xsl:choose>
<xsl:when test="#ItemPosition='1' and Assistant!=''">
<xsl:text>1st: </xsl:text>
</xsl:when>
<xsl:when test="#ItemPosition='2'">
<xsl:text>2nd: </xsl:text>
</xsl:when>
<xsl:when test="#ItemPosition='3'">
<xsl:text>3rd: </xsl:text>
</xsl:when>
</xsl:choose>
<xsl:attribute name="dir">
<xsl:value-of select="//Settings/Direction"/>
</xsl:attribute>
<xsl:value-of select="StudyPoint"/>
</div>
It's perfectly clear that the xsl:choose is creating a text node, and you then follow this with an instruction that creates an attribute. You need to create the attributes of an element before you create the children.

Display empty XML data node as text message

I am fairly new to xml and I'm having a weird problem with templates. For empty data nodes, instead of displaying a blank space where the data should be, I want to display a message like 'None Found' or 'No Data' on an html page. I have done both an xsl:choose statement and 2 xsl:if statements with no success. I already know this particular node is empty, I just want to show a message instead of a blank space. The Otherwise or False statements just don't seem to run at all. Please help!! What am I doing wrong here?
The XML Code
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<findings>
<ssns/>
<dobs>
<dob>
<data>082967</data>
</dob>
</dobs>
<dobs>
<dob>
<data>020568</data>
</dob>
</dobs>
<names>
<name>
<full>Homer J Simpson</full>
<first>Homer</first>
<last>Simpson</last>
<middle>J</middle>
</name>
</names>
<names>
<name>
<full>Marge H Simpson</full>
<first>Marge</first>
<last>Simpson</last>
<middle>H</middle>
</name>
</names>
</findings>
</Root>
The XSL Code
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<hmtl>
<head>
<title>Person Results</title>
</head>
<div style="font-weight:bold">Alias(es):</div>
<xsl:apply-templates select="Root/findings/names"/>
<div style="font-weight:bold">SSNs:</div>
<xsl:apply-templates select="Root/findings/ssns"/>
<div style="font-weight:bold">DOBs:</div>
<xsl:apply-templates select="Root/findings/dobs"/>
</hmtl>
</xsl:template>
<xsl:template match="Root/findings/names">
<p>
<xsl:variable name="nmesHasData" select="boolean(normalize-space(name))"/>
<xsl:for-each select="name">
<xsl:choose>
<xsl:when test="$nmesHasData">
<ul> <xsl:apply-templates select="full"/> </ul>
</xsl:when>
<xsl:otherwise>
<xsl:text>None found</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</p>
</xsl:template>
<xsl:template match="Root/findings/ssns">
<p>
<xsl:variable name="ssnHasData" select="boolean(normalize-space(ssns))"/>
<xsl:for-each select="ssn">
<xsl:choose>
<xsl:when test="$ssnHasData">
<ul><xsl:apply-templates select="data"/></ul>
</xsl:when>
<xsl:otherwise>
<xsl:text>None found</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</p>
</xsl:template>
<xsl:template match="Root/findings/dobs">
<p>
<xsl:variable name="dobHasData" select="boolean(normalize-space(dob))"/>
<xsl:for-each select="dob">
<xsl:choose>
<xsl:when test="$dobHasData">
<ul><xsl:apply-templates select="data"/></ul>
</xsl:when>
<xsl:otherwise>
<xsl:text>None found</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</p>
</xsl:template>
</xsl:transform>
The HTML Result
<!DOCTYPE html
PUBLIC "XSLT-compat">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Person Results</title>
</head>
<div style="font-weight:bold">Alias(es):</div>
<p>
<ul>Homer J Simpson</ul>
</p>
<p>
<ul>Marge H Simpson</ul>
</p>
<div style="font-weight:bold">SSNs:</div>
<p></p>
<div style="font-weight:bold">DOBs:</div>
<p>
<ul>082967</ul>
</p>
<p>
<ul>020568</ul>
</p>
</html>
Change the template match="Root/findings/dobs" to this:
<xsl:template match="Root/findings/dobs">
<p>
<xsl:for-each select="dob">
<xsl:variable name="dobHasData" select="boolean(normalize-space(.))"/>
<xsl:choose>
<xsl:when test="$dobHasData">
<ul><xsl:apply-templates select="data"/></ul>
</xsl:when>
<xsl:otherwise>
<xsl:text>None found</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</p>
</xsl:template>
Or even more template matching like:
<xsl:template match="Root/findings/dobs">
<p>
<xsl:apply-templates select="dob"/>
</p>
</xsl:template>
<xsl:template match="dob">
<xsl:variable name="dobHasData" select="boolean(normalize-space(.))"/>
<xsl:choose>
<xsl:when test="$dobHasData">
<ul><xsl:apply-templates select="data"/></ul>
</xsl:when>
<xsl:otherwise>
<xsl:text>None found</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And this last template could also be splitted in two short ones:
<xsl:template match="dob[normalize-space(.)]">
<ul><xsl:apply-templates select="data"/></ul>
</xsl:template>
<xsl:template match="dob[not(normalize-space(.))]">
<xsl:text>None found</xsl:text>
</xsl:template>

Why doesn't this XSLT code return anything?

This is my XML code:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="2.xsl"?>
<xml_tube>
<utilizadores>
<utilizador username="Guicky" password="futurama96">
<nome>Guilherme Luís</nome>
<birthday>1996-11-28</birthday>
<foto href="guilhas.jpg"/>
</utilizador>
<utilizador username="Daisy" password="woofwoof11">
<nome>Daisy Luís</nome>
<birthday>2011-04-04</birthday>
<foto href="daisy.png"/>
</utilizador>
<utilizador username="Anabela65" password="crumble65">
<nome>Anabela Ribeiro</nome>
<birthday>1965-04-02</birthday>
<foto href="belinha.jpg"/>
</utilizador>
<utilizador username="Izzie" password="lagartixa">
<nome>Isadora Luís</nome>
<birthday>1988-06-27</birthday>
<foto href="izzie.jpg"/>
</utilizador>
</utilizadores>
<videos_pub>
<video id="Vid1" publisher="Daisy" duracao="3:37" data="2016-02-29">
<título>Who let the dogs out? - Baha Men</título>
<thumb>http://i3.ytimg.com/vi/Qkuu0Lwb5EM/hqdefault.jpg</thumb>
<descrição>My favorite song, lol.</descrição>
<hyperlink>https://www.youtube.com/watch?v=Qkuu0Lwb5EM</hyperlink>
<qualidade pixelization="1080p"/>
<qualidade pixelization="480p"/>
<likes liked="Guicky"/>
<related></related>
</video>
<video id="Vid2" publisher="Guicky" duracao="4:01" data="2016-02-29">
<título>Desiigner - Timmy Turner</título>
<thumb>http://i3.ytimg.com/vi/JzmRt2VgKYQ/hqdefault.jpg</thumb>
<descrição>Timmy Timmy Timmy Turner.</descrição>
<hyperlink>https://www.youtube.com/embed/ProbPpO_8oo</hyperlink>
<qualidade pixelization="720p"/>
<qualidade pixelization="1080p"/>
<likes liked="Anabela65"/>
<likes liked="Daisy"/>
<related></related>
</video>
<video id="Vid3" publisher="Guicky" duracao="3:47" data="2016-02-29">
<título>I'm all the way up - Fat Joe</título>
<thumb>https://www.youtube.com/watch?v=y2ak_oBeC-I</thumb>
<descrição>Nothing can stop me, I'm all the way up.</descrição>
<hyperlink>https://www.youtube.com/embed/y2ak_oBeC-I</hyperlink>
<qualidade pixelization="480p"/>
<qualidade pixelization="720p"/>
<related></related>
</video>
<video id="Vid4" publisher="Anabela65" duracao="3:21" data="2016-02-29">
<título>Putzgrilla - Sentadinha</título>
<thumb>http://i3.ytimg.com/vi/QC4JbIvIhdI/hqdefault.jpg</thumb>
<descrição>Dá uma sentadinha.</descrição>
<hyperlink>https://www.youtube.com/watch?v=QC4JbIvIhdI</hyperlink>
<qualidade pixelization="720p"/>
<likes liked="Guicky"/>
<likes liked="Daisy"/>
<related></related>
</video>
<video data="2017-01-13" duracao="02:16" id="Vid5" publisher="Izzie">
<título>Yann Tiersen - La Valse d'Amelie</título>
<thumb>http://i3.ytimg.com/vi/uzurqBnALkw/hqdefault.jpg</thumb>
<descrição>A minha música preferida.</descrição>
<hyperlink>https://www.youtube.com/watch?v=uzurqBnALkw</hyperlink>
<qualidade pixelization="720p"/>
<likes liked="Guicky"/>
<likes liked="Anabela65"/>
<related></related>
</video>
</videos_pub>
<playlist>
<lista owner="Guicky" dataIns="2016-10-24" id="PV1">
<titulo>BEST MUSIC.</titulo>
<descricao>Compilation of my favourite music videos.</descricao>
<gostosL gostouL="Guicky"/>
<links_vid vid="Vid2"/>
<links_vid vid="Vid3"/>
<administradores>
<admin ref="Guicky"/>
<admin ref="Daisy"/>
</administradores>
<editores>
<editor ref="Guicky"/>
</editores>
<subscritores>
<subs ref="Daisy"/>
<subs ref="Anabela65"/>
</subscritores>
<thumbnail link="http://i3.ytimg.com/vi/QC4JbIvIhdI/hqdefault.jpg"/>
</lista>
<lista owner="Anabela65" dataIns="2016-02-29" id="PV2">
<titulo>Sentadinha!</titulo>
<descricao>Siting lesson's with Guicky's mom!</descricao>
<links_vid vid="Vid4"/>
<administradores>
<admin ref="Anabela65"/>
<admin ref="Guicky"/>
</administradores>
<editores>
<editor ref="Anabela65"/>
</editores>
<subscritores>
<subs ref="Guicky"/>
<subs ref="Daisy"/>
</subscritores>
<thumbnail link="http://i3.ytimg.com/vi/uMK0prafzw0/hqdefault.jpg"/>
</lista>
</playlist>
<comentarios>
<comentario id="C1" refV="Vid1" user="Guicky" data="2016-10-23">
<text>AHAHAHAHA, bom vídeo.</text>
<gosto gostou="Daisy"/>
<respostas>
<texto autor="Daisy">Grande clássico!</texto>
</respostas>
</comentario>
<comentario id="C2" refL="Vid2" user="Anabela65" data="2016-10-22">
<text>Timmy timmy timmy turner...</text>
<gosto gostou="Guicky"/>
<gosto gostou="Daisy"/>
<respostas>
<texto autor="Guicky">U know it.</texto>
<resposta autor="Daisy">LOL!</resposta>
</respostas>
</comentario>
</comentarios>
</xml_tube>
And this is my XSLT code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" version="1.0" encoding="UTF-8"
indent="yes" />
<xsl:output doctype-public="-//W3C//DTD XHTML 1.1//EN"
doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" />
<!-- <xsl:imports href="1.xsl"/> -->
<xsl:template match="/">
<html>
<head>
<title>2.</title>
</head>
<body>
<xsl:apply-templates select="//videos_pub"/>
<!-- <xsl:apply-templates select="//comentarios"/> -->
</body>
</html>
</xsl:template>
<xsl:template match="videos_pub">
<xsl:call-template name="publisher">
<xsl:with-param name="id" select="//video/#id"/>
</xsl:call-template>
<xsl:call-template name="data">
<xsl:with-param name="id" select="//video/#id"/>
</xsl:call-template>
<xsl:call-template name="comment">
<xsl:with-param name="id" select="//video/#id"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="publisher">
<xsl:param name="id"/>
<p>
<xsl:value-of select="//video[#id = $id]/./#publisher"/>
</p>
</xsl:template>
<xsl:template name="data">
<xsl:param name="id"/>
<p>
<xsl:value-of select="//video[#id= $id]/./#data"/>
</p>
</xsl:template>
<xsl:template name="comment">
<xsl:param name="id"/>
<p>
<xsl:value-of select="count(//comentario[#refV = $id])" />
</p>
</xsl:template>
</xsl:stylesheet>
It's returning this:
Daisy
2016-02-29
0
This is what I expected it to output:
Daisy
2016-02-29
1
But it isn't returning the one. Why?
Do I have to exit the template //videos_pub?
If so, how do I do it?
I realized by doing the :
<xsl:with-param name="id" select="//video/#id"/>
I was selecting the node itself not the value of the "//video/#id" expression.
I fixed it by doing this:
<xsl:with-param name="id">
<xsl:value-of select="//video/#id"/>
</xsl:with-param>
Now my paramhas the value of the node //video/#id.
Thank you all for your answers.
You are overusing the // syntax, which is a "brute force" way of using XPath when you're not sure of a better way. You're also using "going around your elbow to get to your ***" expressions like //video[#id= $id]/./#data.
Also, I assume you want to output info for all of the videos, not just the first one.
Here is how you would do that, without all the sloppy stuff:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" version="1.0" encoding="UTF-8"
indent="yes" />
<xsl:output doctype-public="-//W3C//DTD XHTML 1.1//EN"
doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" />
<!-- <xsl:imports href="1.xsl"/> -->
<xsl:template match="/">
<html>
<head>
<title>2.</title>
</head>
<body>
<ul>
<xsl:apply-templates select="xml_tube/videos_pub/video"/>
</ul>
<!-- <xsl:apply-templates select="//comentarios"/> -->
</body>
</html>
</xsl:template>
<xsl:template match="video">
<li>
<xsl:call-template name="publisher">
<xsl:with-param name="video" select="."/>
</xsl:call-template>
<xsl:call-template name="data">
<xsl:with-param name="video" select="."/>
</xsl:call-template>
<xsl:call-template name="comment">
<xsl:with-param name="id" select="#id"/>
</xsl:call-template>
</li>
</xsl:template>
<xsl:template name="publisher">
<xsl:param name="video"/>
<p>
<xsl:value-of select="$video/#publisher"/>
</p>
</xsl:template>
<xsl:template name="data">
<xsl:param name="video"/>
<p>
<xsl:value-of select="$video/#data"/>
</p>
</xsl:template>
<xsl:template name="comment">
<xsl:param name="id"/>
<p>
<xsl:value-of select="count(/xml_tube/comentarios/comentario[#refV = $id])" />
</p>
</xsl:template>
</xsl:stylesheet>
And if you want to make it really efficient, you can use an xsl:key to help count the comments:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" version="1.0" encoding="UTF-8"
indent="yes" />
<xsl:output doctype-public="-//W3C//DTD XHTML 1.1//EN"
doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" />
<xsl:key name="kVideoComments" match="comentario" use="#refV" />
<!-- <xsl:imports href="1.xsl"/> -->
<xsl:template match="/">
<html>
<head>
<title>2.</title>
</head>
<body>
<ul>
<xsl:apply-templates select="xml_tube/videos_pub/video"/>
</ul>
<!-- <xsl:apply-templates select="//comentarios"/> -->
</body>
</html>
</xsl:template>
<xsl:template match="video">
<li>
<xsl:call-template name="publisher">
<xsl:with-param name="video" select="."/>
</xsl:call-template>
<xsl:call-template name="data">
<xsl:with-param name="video" select="."/>
</xsl:call-template>
<xsl:call-template name="comment">
<xsl:with-param name="id" select="#id"/>
</xsl:call-template>
</li>
</xsl:template>
<xsl:template name="publisher">
<xsl:param name="video"/>
<p>
<xsl:value-of select="$video/#publisher"/>
</p>
</xsl:template>
<xsl:template name="data">
<xsl:param name="video"/>
<p>
<xsl:value-of select="$video/#data"/>
</p>
</xsl:template>
<xsl:template name="comment">
<xsl:param name="id"/>
<p>
<xsl:value-of select="count(key('kVideoComments', $id))" />
</p>
</xsl:template>
</xsl:stylesheet>
Both of these output:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>2.</title>
</head>
<body>
<ul>
<li>
<p>Daisy</p>
<p>2016-02-29</p>
<p>1</p>
</li>
<li>
<p>Guicky</p>
<p>2016-02-29</p>
<p>0</p>
</li>
<li>
<p>Guicky</p>
<p>2016-02-29</p>
<p>0</p>
</li>
<li>
<p>Anabela65</p>
<p>2016-02-29</p>
<p>0</p>
</li>
<li>
<p>Izzie</p>
<p>2017-01-13</p>
<p>0</p>
</li>
</ul>
</body>
</html>

Wrapping words from HTML using XSL

I need wrapping each word with a tag (e. span) in a HTML document, like:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div> Text in a div </div>
<div>
Text in a div
<p>
Text inside a p
</p>
</div>
</body>
</html>
To result something like this:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div> <span>Text </span> <span> in </span> <span> a </span> <span> div </span> </div>
<div>
<span>Text </span> <span> in </span> <span> a </span> <span> div </span>
<p>
<span>Text </span> <span> in </span> <span> a </span> <span> p </span>
</p>
</div>
</body>
</html>
It's important to keep the structure of the body...
Any help?
All of the three different solutions below use the XSLT design pattern of overriding the identity rule to generally preserve the structure and contents of the XML document, and only modify specific nodes.
I. XSLT 1.0 solution:
This short and simple transformation (no <xsl:choose> used anywhere):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(self::title)]/text()"
name="split">
<xsl:param name="pText" select=
"concat(normalize-space(.), ' ')"/>
<xsl:if test="string-length(normalize-space($pText)) >0">
<span>
<xsl:value-of select=
"substring-before($pText, ' ')"/>
</span>
<xsl:call-template name="split">
<xsl:with-param name="pText"
select="substring-after($pText, ' ')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied to the provided XML document:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div> Text in a div </div>
<div>
Text in a div
<p>
Text inside a p
</p>
</div>
</body>
</html>
produces the wanted, correct result:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div>
<span>Text</span>
<span>in</span>
<span>a</span>
<span>div</span>
</div>
<div>
<span>Text</span>
<span>in</span>
<span>a</span>
<span>div</span>
<p>
<span>Text</span>
<span>inside</span>
<span>a</span>
<span>p</span>
</p>
</div>
</body>
</html>
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(self::title)]/text()">
<xsl:for-each select="tokenize(., '[\s]')[.]">
<span><xsl:sequence select="."/></span>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied to the same XML document (above), again the correct, wanted result is produced:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div>
<span>Text</span>
<span>in</span>
<span>a</span>
<span>div</span>
</div>
<div>
<span>Text</span>
<span>in</span>
<span>a</span>
<span>div</span>
<p>
<span>Text</span>
<span>inside</span>
<span>a</span>
<span>p</span>
</p>
</div>
</body>
</html>
III Solution using FXSL:
Using the str-split-to-words template/function of FXSL one can easily implement much more complicated tokenization -- in any version of XSLT:
Let's have a more complicated XML document and tokenization rules:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div> Text: in a div </div>
<div>
Text; in; a. div
<p>
Text- inside [a] [p]
</p>
</div>
</body>
</html>
Here there is more than one delimiter that indicates the start or end of a word. In this particular example the delimiters can be: " ", ";", ".", ":", "-", "[", "]".
The following transformation uses FXSL for this more complicated tokenization:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext">
<xsl:import href="strSplit-to-Words.xsl"/>
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(self::title)]/text()">
<xsl:variable name="vwordNodes">
<xsl:call-template name="str-split-to-words">
<xsl:with-param name="pStr" select="normalize-space(.)"/>
<xsl:with-param name="pDelimiters"
select="' ;.:-[]'"/>
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
</xsl:template>
<xsl:template match="word[string-length(normalize-space(.)) > 0]">
<span>
<xsl:value-of select="."/>
</span>
</xsl:template>
</xsl:stylesheet>
and produces the wanted, correct result:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div>
<span>Text</span>
<span>in</span>
<span>a</span>
<span>div</span>
</div>
<div>
<span>Text</span>
<span>in</span>
<span>a</span>
<span>div</span>
<p>
<span>Text</span>
<span>inside</span>
<span>a</span>
<span>p</span>
<word/>
</p>
</div>
</body>
</html>
You could achieve this by extending the identity transform to include a recursive template which checks for spaces in a piece of text, and if so puts a span tag around the first word. It can then recursively calls itself for the remaining portion of the text.
Here is it in action...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Don't split the words in the title -->
<xsl:template match="title">
<xsl:copy-of select="." />
</xsl:template>
<!-- Matches a text element. Given a name so it can be recursively called -->
<xsl:template match="text()" name="wrapper">
<xsl:param name="text" select="." />
<xsl:variable name="new" select="normalize-space($text)" />
<xsl:choose>
<xsl:when test="contains($new, ' ')">
<span><xsl:value-of select="concat(substring-before($new, ' '), ' ')" /></span>
<xsl:call-template name="wrapper">
<xsl:with-param name="text" select="substring-after($new, ' ')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<span><xsl:value-of select="$new" /></span>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When called on your sample HTML, the output is as follows:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div>
<span>Text </span>
<span>in </span>
<span>a </span>
<span>div</span>
</div>
<div>
<span>Text </span>
<span>in </span>
<span>a </span>
<span>div</span>
<p>
<span>Text </span>
<span>inside </span>
<span>a </span>
<span>p</span>
</p>
</div>
</body>
</html>
I wasn't 100% sure how important the spaces within the span elements are for you though.

How to format unknown XML elements of a specific class with XSLT into HTML?

So I have a XML document generated by my application like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE AddressBook>
<?xml-stylesheet type="text/xsl" href="stylesheet.xsl"?>
<AddressBook>
<Item>
<UserGeneratedElementName1 class="info">Whatever blah blah</UserGeneratedElementName1>
<UserGeneratedElementName2 class="info">Whatever blah blah</UserGeneratedElementName2>
</Item>
<Item>
<UserGeneratedElementName3 class="info">Whatever blah blah</UserGeneratedElementName3>
</Item>
...
...
Other Items with user-generated elements with user-generated content...
</AddressBook>
And I want to turn it into a HTML document similar to this:
<html>
<head>
<title>AddressBook</title>
</head>
<body>
<div class="root">
<div class="item">
<b>UserGeneratedElementName1:</b> Whatever blah blah
<b>UserGeneratedElementName2:</b> Whatever blah blah
</div>
<div class="item">
<b>UserGeneratedElementName3:</b> Whatever blah blah
</div>
...
...
Other transformed items...
</div>
</body>
</html>
I have tried to get a grasp of the XSLT syntax, but all the guides were either too vague to help me with this or too deep. Also XSLT syntax seems quite confusing.
Thanks in advance.
Take a look at this question here
Is there an XSLT name-of element?
You can use
<xsl:value-of select ="name(.)"/>
or
<xsl:value-of select ="local-name()"/>
to get the name of a node, depending on if you want to include the full prefixed name, or just the local portion.
You should be able to piece those together with xsl:for-each blocks to iterate through the first 3 levels of items and generate the HTML you're looking for.
Something like this would work for a fixed number of levels.
<xsl:for-each select="*">
<html>
<head>
<title><xsl:value-of select="local-name()" /></title>
</head>
<body>
<div class="root">
<xsl:for-each select="*">
<div>
<xsl:attribute name="class">
<xsl:value-of select="local-name()" />
</xsl:attribute>
<xsl:for-each select="*">
<b><xsl:value-of select="local-name()" />:</b> <xsl:value-of select="." />
</xsl:for-each>
</div>
</xsl:for-each>
</div>
</body>
</html>
</xsl:for-each>
A more generic approach would look something more like:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html" indent="yes" version="4.0"/>
<xsl:template match="/">
<xsl:for-each select="*">
<html>
<head>
<title><xsl:value-of select="local-name()" /></title>
</head>
<body>
<div class="root">
<xsl:call-template name="recurseElement">
<xsl:with-param name="element" select="." />
</xsl:call-template>
</div>
</body>
</html>
</xsl:for-each>
</xsl:template>
<xsl:template name="recurseElement">
<xsl:param name="element" />
<xsl:for-each select="$element/*">
<xsl:choose>
<xsl:when test="count(child::*)>0">
<div>
<xsl:attribute name="class">
<xsl:value-of select="local-name()" />
</xsl:attribute>
<xsl:call-template name="recurseElement">
<xsl:with-param name="element" select="." />
</xsl:call-template>
</div>
</xsl:when>
<xsl:otherwise>
<b><xsl:value-of select="local-name()" />:</b> <xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This complete XSLT transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="AddressBook">
<html>
<head>
<title>AddressBook</title>
</head>
<body>
<div class="root">
<xsl:apply-templates/>
</div>
</body>
</html>
</xsl:template>
<xsl:template match="Item">
<div class="item"><xsl:apply-templates/></div>
</xsl:template>
<xsl:template match="Item/*">
<b><xsl:value-of select="name()"/>:</b> <xsl:text/>
<xsl:value-of select="concat(.,'
')"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<AddressBook>
<Item>
<UserGeneratedElementName1 class="info">Whatever blah blah</UserGeneratedElementName1>
<UserGeneratedElementName2 class="info">Whatever blah blah</UserGeneratedElementName2>
</Item>
<Item>
<UserGeneratedElementName3 class="info">Whatever blah blah</UserGeneratedElementName3>
</Item> ... ... Other Items with user-generated elements with user-generated content...
</AddressBook>
produces the wanted, correct result:
<html>
<head>
<title>AddressBook</title>
</head>
<body>
<div class="root">
<div class="item">
<b>UserGeneratedElementName1:</b>Whatever blah blah
<b>UserGeneratedElementName2:</b>Whatever blah blah
</div>
<div class="item">
<b>UserGeneratedElementName3:</b>Whatever blah blah
</div> ... ... Other Items with user-generated elements with user-generated content...
</div>
</body>
</html>
Explanation:
Templates matching any Item element and any element child of an Item element.
Use of the standard XPath name() function.