Being a XML file, I know to generate some output by the XSL. But I need to build a navigation tree either to hidden the contents that I don't have to look at or show those I want to. Tree that I am looking for should look like this one:
+VIEW1
+VIEW2
if hitting something like '+' somewhere, for instance on VIEW2 , we should get the content of VIEW2 like this one:
+VIEW1
-VIEW2
yy NO
aa YES
zz NO
tt NO
Here's a part of my XML file and "view.xsl" which i wrote. I also tried to modify some examples from the stackoverflow but i didn't find how to get right solution.
view.xsl
<xsl:stylesheet version="2.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" cdata-section-elements="Cdata" indent="yes"/>
<xsl:template match = "/">
<html >
<head>
<title Localizable_1="True"><xsl:value-of select="DOC/show"/></title>
</head>
<BODY class="BODY">
<H1><xsl:value-of select="DOC/show"/></H1>
<TABLE WIDTH="500px">
<xsl:for-each select="DOC/Entry">
<xsl:call-template name="RULE"/>
</xsl:for-each>
</TABLE>
</BODY>
</html>
</xsl:template>
<xsl:template name="RULE">
<xsl:choose>
<xsl:if test="level='ON'"><xsl:value-of select="light"/>YES</xsl:if>
<xsl:if test="level='OFF'"><xsl:value-of select="light"/>NO</xsl:if>
</xsl:choose>
file.XML
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml:stylesheet type='text/xsl' href='view.xsl'?>
<DOC>
<show>VIEW1</show>
<Entry>
<light>ae</light>
<level>ON</level>
</Entry>
<Entry>
<light>by</light>
<level>OFF</level>
</Entry>
<Entry>
<light>ac</light>
<level>OFF</level>
</Entry>
<show>VIEW2</show>
<Entry>
<light>yy</light>
<level>OFF</level>
</Entry>
<Entry>
<light>aa</light>
<level>ON</level>
</Entry>
<Entry>
<light>zz</light>
<level>OFF</level>
</Entry>
<Entry>
<light>tt</light>
<level>OFF</level>
</Entry>
</DOC>
Thanks for any help
This works (I build a table with tr and td elements but I assume you can enhance from here).
Helpful links on following-sibling
<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" cdata-section-elements="Cdata" indent="yes"/>
<xsl:template match = "/">
<html >
<head>
<title Localizable_1="True">
<xsl:value-of select="DOC/show"/>
</title>
<script src="http://code.jquery.com/jquery-1.9.1.min.js" type="text/javascript">
<xsl:comment>jq</xsl:comment>
</script>
<script src="https://raw.github.com/vakata/jstree/master/dist/jstree.min.js" type="text/javascript">
<xsl:comment>tree</xsl:comment>
</script>
</head>
<body class="BODY">
<div class="tree">
<xsl:apply-templates select="DOC/show" />
</div>
<script>
$('div.tree')
.jstree({
core: {}
})
.bind("select_node.jstree", function (event, data) {
alert(data.node.context.id); /* do clever things here */
})
.delegate("a", "click", function (event, data) { event.preventDefault(); });
</script>
</body>
</html>
</xsl:template>
<!-- match show elements -->
<xsl:template match="show">
<ul class="tree">
<li>
<a>
<xsl:value-of select="."/>
</a>
<ul>
<!-- only select the next Entry element -->
<xsl:apply-templates select="following-sibling::*[1][self::Entry] "/>
</ul>
</li>
</ul>
</xsl:template>
<xsl:template match="Entry">
<li class="rule">
<xsl:call-template name="RULE"/>
</li>
<!-- only select the next Entry element -->
<xsl:apply-templates select="following-sibling::*[1][self::Entry] "/>
</xsl:template>
<xsl:template name="RULE">
<xsl:element name="a">
<!-- or have a href here -->
<xsl:attribute name="id">
<xsl:value-of select="light"/>
</xsl:attribute>
<span class="light">
<xsl:value-of select="light"/>
</span>
<span class="level">
<xsl:choose>
<xsl:when test="level='ON'">
<xsl:text>YES</xsl:text>
</xsl:when>
<xsl:when test="level='OFF'">
<xsl:text>NO</xsl:text>
</xsl:when>
</xsl:choose>
</span>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Related
I am trying to define some dynamically created elements as cdata sections, but it's not working for some reason:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes" method="xml"
cdata-section-elements="DESCRIPTION2"
/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/RSS/ITEM/TEST">
<DESCRIPTION2>
<div class="container">
<xsl:if test="NAME != ''">
<div class="test">
<xsl:value-of select="NAME"/>
</div>
</xsl:if>
</div>
</DESCRIPTION2>
</xsl:template>
</xsl:stylesheet>
Test XML:
<?xml version="1.0" encoding="UTF-8"?>
<RSS>
<ITEM>
<CODE>41,000</CODE>
<TEST>
<NAME><p>HTML code</p></NAME>
</TEST>
</ITEM>
</RSS>
Live test.
Sure I can add manually (<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>), but I would like to know why it's not working If I define it as cdata-section-elements.
CDATA serialization happens for text nodes inside of the nominated elements, if you put in elements there it doesn't happen. Note that, assuming an XSLT 3 processor with XPath 3.1 support, you can use the serialize function to serialize the content you build as html and then output it as a text node:
<xsl:template match="/RSS/ITEM/TEST">
<xsl:variable name="html">
<div class="container">
<xsl:if test="NAME != ''">
<div class="test">
<xsl:value-of select="NAME"/>
</div>
</xsl:if>
</div>
</xsl:variable>
<DESCRIPTION2>
<xsl:value-of select="serialize($html, map { 'method' : 'html' })"/>
</DESCRIPTION2>
</xsl:template>
http://xsltfiddle.liberty-development.net/948Fn5i/1 then gives the result as a CDATA section
<DESCRIPTION2><![CDATA[<div class="container">
<div class="test">Peter</div>
</div>]]></DESCRIPTION2>
Your content is well-formed XHTML, so it doesn't need to apply CDATA when serializing the content.
If you escaped the markup and constructed a string, it would serialize as CDATA:
<xsl:template match="/RSS/ITEM/TEST">
<DESCRIPTION2>
<div class="container">
<xsl:if test="NAME != ''">
<div class="test">
<xsl:value-of select="NAME"/>
</div>
</xsl:if>
</div>
</DESCRIPTION2>
</xsl:template>
Produces:
<DESCRIPTION2><![CDATA[
<div class="container">
<div class="test">
Peter
</div>
</div>
]]></DESCRIPTION2>
But why would you want to generate a string when you could have well-formed markup? It makes it a pain for everyone downstream.
I'm writing a XSLT script and there is a section named footnotes, my script is catching all elements other than footnotes. I'm using XSL2.0 and Below is my XML
<?xml version="1.0" encoding="utf-8"?>
<chapter num="D" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:acknowledgement="file://acknowledgement.xsd" xmlns:admission="file://admission.xsd" xmlns:affidavit="file://affidavit.xsd" xmlns:agreement="file://agreement.xsd" xmlns:appeal="file://appeal.xsd" xmlns:appearance="file://appearance.xsd" xmlns:application="file://application.xsd" xmlns:assent="file://assent.xsd" xmlns:authorisation="file://authorisation.xsd" xmlns:award="file://award.xsd" xmlns:brief="file://brief.xsd" xmlns:caveat="file://caveat.xsd" xmlns:certificate="file://certificate.xsd" xmlns:checklist="file://checklist.xsd" xmlns:claim="file://claim.xsd" xmlns:clause="file://clause.xsd" xmlns:comparisontable="file://comparisontable.xsd" xmlns:conditionsofcontract="file://conditionsofcontract.xsd" xmlns:declaration="file://declaration.xsd" xmlns:defaultjudgment="file://defaultjudgment.xsd" xmlns:defence="file://defence.xsd" xmlns:demand="file://demand.xsd" xmlns:flowchart="file://flowchart.xsd" xmlns:form="file://form.xsd" xmlns:guarantee="file://guarantee.xsd" xmlns:instruction="file://instruction.xsd" xmlns:judgment="file://judgment.xsd" xmlns:letter="file://letter.xsd" xmlns:minutes="file://minutes.xsd" xmlns:notice="file://notice.xsd" xmlns:oath="file://oath.xsd" xmlns:order="file://order.xsd" xmlns:patent="file://patent.xsd" xmlns:petition="file://petition.xsd" xmlns:powerofattorney="file://powerofattorney.xsd" xmlns:praecipe="file://praecipe.xsd" xmlns:questionnaire="file://questionnaire.xsd" xmlns:recognisance="file://recognisance.xsd" xmlns:release="file://release.xsd" xmlns:renunciation="file://renunciation.xsd" xmlns:reply="file://reply.xsd" xmlns:report="file://report.xsd" xmlns:request="file://request.xsd" xmlns:schedule="file://schedule.xsd" xmlns:statementofclaim="file://statementofclaim.xsd" xmlns:subpoena="file://subpoena.xsd" xmlns:summons="file://summons.xsd" xmlns:undertaking="file://undertaking.xsd" xmlns:warrant="file://warrant.xsd" xmlns:writ="file://writ.xsd" xmlns:book="file://book.xsd" xmlns:forms="file://forms.xsd" xmlns:misc="file://misc.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:htm="http://www.w3.org/TR/html5/">
<htm:p align="center">
<htm:b>Bold Content</htm:b>
<footnote num="1">
<para>
Footnote content.
</para>
</footnote>
</htm:p>
</chapter>
And below is my XSLT.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ntw="Number2Word.uri" xmlns:altova="http://www.altova.com/xslt-extensions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:acknowledgement="file://acknowledgement.xsd" xmlns:admission="file://admission.xsd" xmlns:affidavit="file://affidavit.xsd" xmlns:agreement="file://agreement.xsd" xmlns:appeal="file://appeal.xsd" xmlns:appearance="file://appearance.xsd" xmlns:application="file://application.xsd" xmlns:assent="file://assent.xsd" xmlns:authorisation="file://authorisation.xsd" xmlns:award="file://award.xsd" xmlns:brief="file://brief.xsd" xmlns:caveat="file://caveat.xsd" xmlns:certificate="file://certificate.xsd" xmlns:checklist="file://checklist.xsd" xmlns:claim="file://claim.xsd" xmlns:clause="file://clause.xsd" xmlns:comparisontable="file://comparisontable.xsd" xmlns:conditionsofcontract="file://conditionsofcontract.xsd" xmlns:declaration="file://declaration.xsd" xmlns:defaultjudgment="file://defaultjudgment.xsd" xmlns:defence="file://defence.xsd" xmlns:demand="file://demand.xsd" xmlns:flowchart="file://flowchart.xsd" xmlns:form="file://form.xsd" xmlns:guarantee="file://guarantee.xsd" xmlns:instruction="file://instruction.xsd" xmlns:judgment="file://judgment.xsd" xmlns:letter="file://letter.xsd" xmlns:minutes="file://minutes.xsd" xmlns:notice="file://notice.xsd" xmlns:oath="file://oath.xsd" xmlns:order="file://order.xsd" xmlns:patent="file://patent.xsd" xmlns:petition="file://petition.xsd" xmlns:powerofattorney="file://powerofattorney.xsd" xmlns:praecipe="file://praecipe.xsd" xmlns:questionnaire="file://questionnaire.xsd" xmlns:recognisance="file://recognisance.xsd" xmlns:release="file://release.xsd" xmlns:renunciation="file://renunciation.xsd" xmlns:reply="file://reply.xsd" xmlns:report="file://report.xsd" xmlns:request="file://request.xsd" xmlns:schedule="file://schedule.xsd" xmlns:statementofclaim="file://statementofclaim.xsd" xmlns:subpoena="file://subpoena.xsd" xmlns:summons="file://summons.xsd" xmlns:undertaking="file://undertaking.xsd" xmlns:warrant="file://warrant.xsd" xmlns:writ="file://writ.xsd" xmlns:book="file://book.xsd" xmlns:forms="file://forms.xsd" xmlns:misc="file://misc.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:htm="http://www.w3.org/TR/html5/" xpath-default-namespace="http://foo.bar.com/ns" exclude-result-prefixes="#all">
<xsl:output method="html"/>
<xsl:strip-space elements="*"/>
<!--<xsl:preserve-space elements="para"/>-->
<xsl:variable name="nums" as="element()+">
<ntw:nums num="1" word="first"/>
<ntw:nums num="2" word="second"/>
<ntw:nums num="3" word="third"/>
<ntw:nums num="4" word="forth"/>
<ntw:nums num="5" word="fifth"/>
<ntw:nums num="6" word="sixth"/>
<ntw:nums num="7" word="seventh"/>
<ntw:nums num="8" word="eighth"/>
<ntw:nums num="9" word="nighth"/>
<ntw:nums num="10" word="tenth"/>
</xsl:variable>
<xsl:template match="/">
<xsl:text disable-output-escaping="yes"><![CDATA[<!DOCTYPE html>]]></xsl:text>
<html>
<head>
<xsl:text disable-output-escaping="yes"><![CDATA[</meta>]]></xsl:text>
<title>
<xsl:value-of select="chapter/title/text()"/>
</title>
<link rel="stylesheet" href="C:\Users\u0138039\Desktop\Proview\MY\2016\CCA3ED\XML\XSLT\main.css" type="text/css"/>
<xsl:text disable-output-escaping="yes"><![CDATA[</link>]]></xsl:text>
</head>
<body>
<section class="tr_chapter">
<xsl:apply-templates/>
</section>
<xsl:if test="//footnote">
<section class="tr_footnotes">
<xsl:text disable-output-escaping="yes"><![CDATA[<hr />]]></xsl:text>
<xsl:apply-templates select="//page | //footnote" mode="footnote"/>
</section>
</xsl:if>
</body>
</html>
</xsl:template>
<xsl:template match="chapter">
<xsl:apply-templates select="descendant::title[1]/page" mode="first"/>
<div class="chapter">
<xsl:variable name="num_S">
<xsl:value-of select="."/>
</xsl:variable>
<a name="CLIHK_CH_{$num_S}"/>
<div class="chapter-title">
<span class="chapter-num">
<xsl:value-of select="normalize-space(concat('Chapter ',format-number(./#num,'0')))"/>
</span>
<xsl:text disable-output-escaping="yes"><![CDATA[<br /><br />]]></xsl:text>
<xsl:apply-templates select="title/node() except descendant-or-self::page"/>
<xsl:text disable-output-escaping="yes"><![CDATA[<br /><br />]]></xsl:text>
<xsl:apply-templates select="chapter-meta"/>
</div>
<xsl:apply-templates select="child::node()[not(self::title|self::chapter-meta)]"/>
</div>
</xsl:template>
<xsl:template match="htm:b">
<span class="font-style-bold">
<xsl:apply-templates/>
</span>
</xsl:template>
<xsl:template match="htm:p">
<xsl:apply-templates select="./node()[1][self::page]" mode="first"/>
<div>
<xsl:choose>
<xsl:when test="./#align">
<xsl:attribute name="class"><xsl:text>para align-</xsl:text><xsl:value-of select="./#align"/></xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="class"><xsl:text>para</xsl:text></xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="footnote">
<xsl:variable name="cnt" select="count(preceding::footnote)+1"/>
<xsl:variable name="varHeaderNote" select='concat("f",$cnt)'/>
<xsl:variable name="varFootNote" select='concat("#ftn.",$cnt)'/>
<sup>
<a name="{$varHeaderNote}" href="{$varFootNote}" class="tr_ftn">
<xsl:value-of select="#num"/>
</a>
</sup>
</xsl:template>
<xsl:template match="footnote" mode="footnote">
<xsl:variable name="cnt" select="count(preceding::footnote)+1"/>
<div class="tr_footnote">
<div class="footnote">
<sup>
<a>
<xsl:attribute name="name"><xsl:text>ftn.</xsl:text><xsl:value-of select="$cnt"/></xsl:attribute>
<xsl:attribute name="href"><xsl:text>#f</xsl:text><xsl:value-of select="$cnt"/></xsl:attribute>
<xsl:attribute name="class"><xsl:text>tr_ftn</xsl:text></xsl:attribute>
<xsl:value-of select="#num"/>
</a>
</sup>
<xsl:apply-templates/>
</div>
</div>
</xsl:template>
</xsl:stylesheet>
My current output is as below
<!DOCTYPE html><html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
<title></title>
<link rel="stylesheet" href="C:\Users\u0138039\Desktop\Proview\MY\2016\CCA3ED\XML\XSLT\main.css" type="text/css"></link>
</head>
<body>
<section class="tr_chapter">
<div class="para align-center"><span class="font-style-bold">Bold Content</span>
Footnote content.
</div>
</section>
</body>
</html>
But my expected output is
<!DOCTYPE html><html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
<title></title>
<link rel="stylesheet" href="C:\Users\u0138039\Desktop\Proview\MY\2016\CCA3ED\XML\XSLT\main.css" type="text/css"></link>
</head>
<body>
<section class="tr_chapter">
<div class="para align-center"><span class="font-style-bold">Bold Content</span><sup>
<a name="f1" href="#ftn.1" class="tr_ftn">1</a>
</sup>
</div>
</section>
<section class="tr_footnotes"><hr/>
<div class="tr_footnote">
<div class="footnote">
<sup>
<a name="ftn.1" href="#f1" class="tr_ftn">1</a>
</sup>
<div class="para">Footnote content.
</div>
</div>
</div></section>
</body>
</html>
Here is a working demo
please let me know where Am I going wrong and how can I fix this.
Thanks
Your stylesheet declares:
xpath-default-namespace="http://foo.bar.com/ns"
The footnote in your XML is in no namespace, therefore the test in:
<xsl:if test="//footnote">
returns false.
My scenario is that we have to check for a div tag in the cdata of a body element. If div is present we have to insert the the text from node2 into the div tag.
This is my input xml:
<?xml version="1.0" encoding="utf-8"?>
<root>
<node1>abc</node1>
<node2> needs to replace inside cdata div</node2>
<body> <![CDATA[
<p>some text some textabcabcabcabc</p>
<div class="marginBottom_4px">
</div>
<p>some text some textabcabc</P>
]]>
</body>
</root>
The out put xml would be:
<?xml version="1.0" encoding="utf-8"?>
<div class="marginBottom_10px">
abc
</div>
<div class="marginBottom_5px">
<p>some text some textabcabcabcabc</p>
<div class="marginBottom_4px">
needs to replace inside cdata div
</div>
<p>some text some textabcabc</P>
</div>
My transform is:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:value-of disable-output-escaping="yes" select ="$firstnode"/>
<xsl:text disable-output-escaping="yes"><![CDATA[ <div class="marginBottom_10px">
]]>
</xsl:text>
<xsl:value-of disable-output-escaping ="yes" select="root/body"/>
<xsl:text disable-output-escaping="yes"><![CDATA[
</div>
]]>
</xsl:text>
</xsl:template>
<xsl:variable name="firstnode">
<xsl:text disable-output-escaping="yes"><![CDATA[
<div class="marginBottom_10px">
]]>
</xsl:text>
<xsl:value-of disable-output-escaping ="yes" select="root/node1"/>
<xsl:text disable-output-escaping="yes"><![CDATA[
</div>
]]>
</xsl:text>
</xsl:variable>
</xsl:stylesheet>
I am able to produce the out put. but my xml is very complex like below:
<?xml version="1.0" encoding="utf-8" ?>
<ComplexXML>
<environment>
couple of nodes..
</environment>
<document>
nodes
</document>
<element cd="dsjdhfjk" input="abc.xml" mode="" >
<cd position="1">
<attributes>
<type>dummy text</type>
<title>dummy text</title>
</attributes>
<content>
<node2>
<![CDATA[
needs to replace inside cdata div
]]>
</node2>
<body>
<![CDATA[
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a type
specimen book </p>
<div class="marginBottom_4px">
</div>
<p>Lorem Ipsum is simply dummy text of her including versions of Lorem Ipsum. </p>
]]>
</body>
<abt >
<![CDATA[
text from abt node
]]>
</abt>
</content>
</cd>
</element>
</ComplexXML>
In the above xml I have to check for the abt node.If data is there in abt node the out should be like below:
<?xml version="1.0" encoding="UTF-8"?>
<div>
text from abt node
<div class="marginBottom_5px">
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a type
specimen book </p>
**<div class="marginBottom_4px">
</div>** I need to remove this div tag and place the node2 content here.
<p>Lorem Ipsum is simply dummy text of her including versions of Lorem Ipsum. </p>
</div>
</div>
Sorry to bother you..I am very new to xslt..I am in learning stage only..Can you please guide me..
The following XSLT 1.0 stylesheet produces what I believe the desired output is, based upon the example output and comments. It relies upon the text in the CDATA of the input document being well-formed, and leverages disable-output-escaping:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="node1">
<div class="marginBottom_10px">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="node2" />
<xsl:template match="body">
<div class="marginBottom_5px">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="body/text()[contains(.,'<div') and contains(.,'</div>')]">
<xsl:value-of
disable-output-escaping="yes"
select="substring-before(.,'</div')" />
<xsl:value-of select="../../node2"/>
<xsl:value-of
disable-output-escaping="yes"
select="substring-after(.,substring-before(.,'</div'))" />
</xsl:template>
</xsl:stylesheet>
When applied against the example input, produces:
<?xml version="1.0" encoding="UTF-8"?>
<div class="marginBottom_10px">abc</div>
<div class="marginBottom_5px">
<p>some text some textabcabcabcabc</p>
<div class="marginBottom_4px">
needs to replace inside cdata div</div>
<p>some text some textabcabc</P>
</div>
Note: the output is not well-formed. There is no document element. You would likely want to create a template for the <root> to create a <div> or other containing element.
This version handles the other input format and generates what I believe the desired output is:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<!--rely on built-in templates,
which apply-templates to child nodes of elements
and get value-of for text() -->
<xsl:template match="content">
<xsl:choose>
<!-- if <abt> has a value, do the following -->
<xsl:when test="normalize-space(abt)">
<div>
<!-- apply templates to <abt>,
built-in template will copy text to output-->
<xsl:apply-templates select="abt"/>
<!-- apply templates to <body>, template defined below will handle it -->
<xsl:apply-templates select="body"/>
</div>
</xsl:when>
<xsl:otherwise>
<!--process child nodes -->
<xsl:apply-templates />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="node1">
<div class="marginBottom_10px">
<xsl:apply-templates/>
</div>
</xsl:template>
<!--empty template ensures that no content produced when templates applied to <node2>-->
<xsl:template match="node2" />
<xsl:template match="body">
<div class="marginBottom_5px">
<xsl:apply-templates/>
</div>
</xsl:template>
<!--Template for handling body/text() when <abt> does not have a value-->
<xsl:template match="*[not(normalize-space(abt))]/body/text()[contains(.,'<div') and contains(.,'</div>')]">
<!--get the value of content preceding "</div"-->
<xsl:value-of
disable-output-escaping="yes"
select="substring-before(.,'</div')" />
<!--get the value of <node2> -->
<xsl:value-of select="../../node2"/>
<!--get the value of content starting at "</div" -->
<xsl:value-of
disable-output-escaping="yes"
select="substring-after(.,substring-before(.,'</div'))" />
</xsl:template>
<!--Template for handling body/text() when <abt> does have a value -->
<xsl:template match="*[normalize-space(abt)]/body/text()[contains(.,'<div') and contains(.,'</div>')]">
<!--get the value preceding "<div" -->
<xsl:value-of
disable-output-escaping="yes"
select="substring-before(.,'>div')" />
<xsl:value-of select="../../node2"/>
<!--get the value following "</div>" -->
<xsl:value-of
disable-output-escaping="yes"
select="substring-after(.,'</div>')" />
</xsl:template>
</xsl:stylesheet>
I'm looking to get some XSLT (for Umbraco CMS) coded properly and I'm getting kind of stumped. What I'm trying to do is:
Start from a certain node, put each child node into a div; for every 3 children, wrap in a parent div.
Instead of my mess of for-each,choose and when statements, I have tried implementing a apply-template structure but I just can't seem to get the hang of it; so here's my mess of XSLT right now (I'm sure this is bad practice & terrible for performance, but I really don't know what to do at the moment):
<div class="row four">
<h2>Smart Phones see all smart phones →</h2>
<div class="row three"> <!-- This div should be created again for every 3 divs -->
<xsl:for-each select="umbraco.library:GetXmlNodeById('1063')/descendant::*[#isDoc and string(showInMainNavigation) = '1']">
<xsl:choose>
<xsl:when test="position() < 3">
<div class="col">
<a href="{umbraco.library:NiceUrl(./#id)}">
<img class="phonePreviewImg" src="{./previewImage}" style="max-width:117px; max-height:179px;" />
<h4 class="phoneTitle"><xsl:value-of select="./#nodeName" />/h4>
<p class="phonePrice">$<xsl:value-of select="./price" /></p
</a>
</div>
</xsl:when>
<xsl:when test="position() = 3"> <!-- set this div to include class of `omega` -->
<div class="col omega">
<a href="{umbraco.library:NiceUrl(./#id)}">
<img class="phonePreviewImg" src="{./previewImage}" style="max-width:117px; max-height:179px;" />
<h4 class="phoneTitle"><xsl:value-of select="./#nodeName" />/h4>
<p class="phonePrice">$<xsl:value-of select="./price" /></p
</a>
</div>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</div> <!-- End Row Three -->
</div> <!-- End Row Four -->
Obviously this code does not produce the "wrap every three". Can anyone shed some light on what I need to do to accomplish this?
UPDATE - improved the answer
I cannot think of an elegant solution using templates, but this clunky one with a loop works:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="render">
<xsl:param name="node"/>
<xsl:param name="last"/>
<div>
<xsl:if test="$last">
<xsl:attribute name="class">
<xsl:text>omega</xsl:text>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="$node"/>
</div>
</xsl:template>
<xsl:template match="/*">
<root>
<xsl:variable name="nodes" select="*[not(#skip)]"/>
<xsl:for-each select="$nodes">
<xsl:if test="(position() mod 3)=1">
<xsl:variable name="position" select="position()"/>
<div>
<xsl:call-template name="render">
<xsl:with-param name="node" select="."/>
<xsl:with-param name="last" select="false()"/>
</xsl:call-template>
<xsl:if test="$nodes[$position+1]">
<xsl:call-template name="render">
<xsl:with-param name="node" select="$nodes[$position+1]"/>
<xsl:with-param name="last" select="false()"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="$nodes[$position+2]">
<xsl:call-template name="render">
<xsl:with-param name="node" select="$nodes[$position+2]"/>
<xsl:with-param name="last" select="true()"/>
</xsl:call-template>
</xsl:if>
</div>
</xsl:if>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
applied to:
<root>
<node>1</node>
<node skip="1">to be skipped</node>
<node>2</node>
<node>3</node>
<node skip="1">to be skipped</node>
<node skip="1">to be skipped</node>
<node>4</node>
<node skip="1">to be skipped</node>
<node>5</node>
<node>6</node>
<node>7</node>
<node skip="1">to be skipped</node>
</root>
produces:
<root>
<div>
<div>1</div>
<div>2</div>
<div class="omega">3</div>
</div>
<div>
<div>4</div>
<div>5</div>
<div class="omega">6</div>
</div>
<div>
<div>7</div>
</div>
</root>
You need to replace the select used to set the $nodes variable the XPath selecting the nodes you want, and the render template with the code needed to generate the result you need for each node.
As simple and short as this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="num[position() mod 3 = 1]">
<div>
<xsl:apply-templates mode="inGroup"
select=".|following-sibling::*[not(position() >2)]"/>
</div>
</xsl:template>
<xsl:template match="num" mode="inGroup">
<p><xsl:apply-templates mode="inGroup"/></p>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
the wanted, correct result is produced:
<div>
<p>01</p>
<p>02</p>
<p>03</p>
</div>
<div>
<p>04</p>
<p>05</p>
<p>06</p>
</div>
<div>
<p>07</p>
<p>08</p>
<p>09</p>
</div>
<div>
<p>10</p>
</div>
Here's an elegant solution using templates.
When this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:param name="pNumInGroup" select="3" />
<xsl:template match="/*">
<html>
<xsl:apply-templates select="*[position() mod $pNumInGroup = 1]" />
</html>
</xsl:template>
<xsl:template match="node">
<div>
<xsl:for-each
select=".|following-sibling::*[not(position() > $pNumInGroup - 1)]">
<div>
<xsl:apply-templates />
</div>
</xsl:for-each>
</div>
</xsl:template>
</xsl:stylesheet>
...is applied to the sample XML provided by #MiMo:
<root>
<node>1</node>
<node>2</node>
<node>3</node>
<node>4</node>
<node>5</node>
<node>6</node>
<node>7</node>
</root>
...the correct result is produced:
<html>
<div>
<div>1</div>
<div>2</div>
<div>3</div>
</div>
<div>
<div>4</div>
<div>5</div>
<div>6</div>
</div>
<div>
<div>7</div>
</div>
</html>
If the parameter value is changed to 5:
<xsl:param name="pNumInGroup" select="5" />
...the correct result is still produced:
<html>
<div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
<div>
<div>6</div>
<div>7</div>
</div>
</html>
Explanation:
We define a pNumInGroup parameter at the top of the document (with a default value of 3). This is useful, as it allows the XSLT to be used more flexibly (i.e., if you need a different number of <div> elements per group, simply pass them as a parameter).
The first template matches the root node, recreates it, and tells the XSLT processor to apply templates to the first element of each grouping (here's a refresher in modular arithmetic in case you need it).
The second template matches the <node> elements that are selected by the previous template. For each, a new <div> element is created and populated with the remaining items appropriate to that wrapped group.
NOTE: I generally stay away from <xsl:for-each> unless I really need it; in the case of the last template, I don't really need it (I could just as easily have specified the wrapping/secondary <div> logic with another template). However, for the sake of "crispness" and not over-templating the XSLT, I chose this route.
i'm having this error when i tried to validate my XSLT
javax.xml.transform.TransformerConfigurationException:
javax.xml.transform.TransformerException:
javax.xml.transform.TransformerException:
A node test that matches either NCName:* or QName was expected.
this is my XSLT
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="html" />
<xsl:template match="\Apps">
<html>
<head> <title>Apps List</title>
<link rel="StyleSheet" href="table_style.css" type="text/css"/>
<style type="text/css">
body {font-family: Helvetca;}
h1 { color : Grey;}
h2 {color : Blue;}</style>
</head>
<body>
<h1> Apps List: <xsl:value-of select="\#List_Type" /></h1>
<p>This is a list of all currently hot apps:</p>
<xsl:for-each select="\App">
<xsl:if test="\App\#installed == true">
<h2 style="color:Green;"><xsl:value-of select="\App\app_name" />(instaled)</h2>
</xsl:if>
<xsl:otherwise>
<h2><xsl:value-of select="\App\app_name" /></h2>
</xsl:otherwise>
<p style="font-style:bold;">App info:</p>
<table id="#gradient-style">
<tr><th>Category:</th><td><xsl:value-of select="\App\catogry" /></td></tr>
<tr><th>Verdion:</th><td><xsl:value-of select="\App\version" /></td></tr>
<tr><th>Description:</th><td><xsl:value-of select="\App\description" /></td></tr>
<tr><th>App Reviews:</th><td><xsl:for-each select="\App\reviews\review">
<span style="font-style:bold;"><xsl:value-of select="\App\reviews\review\reviewer_name" /></span>
| <xsl:value-of select="\App\reviews\review\review_date" />
| <xsl:value-of select="\App\reviews\review\review_Time" /><br/>
<span style="font-style:bold;">Rating:</span>
<xsl:value-of select="string(\App\reviews\review\rating" /> <br/>
<xsl:value-of select="\App\reviews\review\ontent" /><br/>
----------------------------------------------------------
</xsl:for-each>
</td></tr>
</table>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
this is the XML that tried with
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="ShdenXSLT.xsl"?>
<Apps xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" List_Type="new releases" >
<App device_type="tablet" app_id="120">
<app_name>Meeting Manager</app_name>
<catogry>LifeStyle </catogry>
<catogry>Bussnisse </catogry>
<version>1.0</version>
<description>This app is about managing the bussnisse meeting</description>
<reviews>
<review>
<reviewer_name>Shaden</reviewer_name>
<review_date>2012-02-13</review_date>
<review_time>11:35:02</review_time>
<content>it was a useful app</content>
<rating>4.5</rating>
</review>
<review>
<reviewer_name>Mohamed</reviewer_name>
<review_date>2012-03-01</review_date>
<review_time>12:15:00</review_time>
<content>i really loved this app</content>
<rating>5.0</rating>
</review>
</reviews>
</App>
<App device_type="tablet" app_id="100">
<app_name>ToDoList</app_name>
<catogry>LifeStyle </catogry>
<version>3.4.2</version>
<description>a simple To Do List applecation</description>
<reviews>
<review>
<reviewer_name>Fahad</reviewer_name>
<review_date>2010-02-05</review_date>
<review_time>09:40:55</review_time>
<content>nice app</content>
<rating>4.0</rating>
</review>
</reviews>
</App>
</Apps>
You are using backslash (\) as your XPath separator (i.e. <xsl:value-of select="\#List_Type" />), which is incorrect. It should be a forward slash (/)