I've basically got an XML input structure like this:
...
<box type="rectangle" class="someOriginalClass">
<background bgtype="solid" />
<animation order="3" />
... children
</box>
and would like to transform it to
<div class="someOriginalClass rectangle solid animated order3">
...children
</div>
Note that neither background nor animation need to be there, and this is a reduced example, meaning that there could be more properties like these, with more attributes.
As well, animation and background are reused elsewhere.
My XSLT code so far is:
<xsl:template match="box">
<div class="{#someOldClass} {#type}">
<xsl:apply-templates select="./*" />
</div>
</xsl:template>
<xsl:template match="background">
<xsl:attribute name="class">
<xsl:value-of select="#bgtype"/>
</xsl:attribute>
</xsl:template>
<xsl:template match="animation">
<xsl:attribute name="class">
animated order<xsl:value-of select="#order"/>
</xsl:attribute>
</xsl:template>
The problem with this code is that each template overrides the class attribute completely, dismissing already contained classes.
To solve this, I've tried:
a) rewriting old classes => value-of only gets input XML class (someOldClass)
<xsl:template match="animation">
<xsl:attribute name="class">
<xsl:value-of select="../#class"/>
animated order<xsl:value-of select="#order"/>
</xsl:attribute>
</xsl:template>
b) instead passing changes between templates with params => only one time, one way
<xsl:template match="box">
<div class="{#someOldClass} {#type}">
<xsl:apply-templates select="./*">
<xsl:with-param name="class" select="concat(#someOldClass,' ',#type)"/>
</xml:apply-templates>
</div>
</xsl:template>
<xsl:template match="animation">
<xsl:param name="class"/>
<xsl:attribute name="class">
<xsl:value-of select="$class"/>
animated order<xsl:value-of select="#order"/>
</xsl:attribute>
</xsl:template>
You see, I'm lacking a solution that will work with any number of class updates, with minimal redundancy.
BTW, I'm an XSLT beginner, so maybe there's some predestined feature that I've simply not yet come across.
Any ideas?
I've used this before. I'm not sure what your XML looks like but this may help get you on the right path.
<xsl:template match="box">
<xsl:param name="boxType" />
<li>
<xsl:variable name="boxClass">
<xsl:value-of select="$boxType"/>
<xsl:if test="#class1 = 1"> class1</xsl:if>
<xsl:if test="#class2 = 1"> class2</xsl:if>
<xsl:if test="#class3 = 1"> class3</xsl:if>
<xsl:if test="#class4 = 1"> class4</xsl:if>
<xsl:if test="#last = 1"> lnLast</xsl:if>
</xsl:variable>
<xsl:attribute name="class">
<xsl:value-of select="$boxClass"/>
</xsl:attribute>
</li>
</xsl:template>
Related
I'm working on a DDR Treeview menu for DotNetNuke to display only the selected Root items and its child node to be expanded. Here is what I'm trying to achieve.
(Left vertical menu)
Any advice please?
This is the xslt code and is currently displaying all root items.
<?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:param name="ControlID" />
<xsl:param name="Options" />
<xsl:template match="/*">
<xsl:apply-templates select="root" />
</xsl:template>
<xsl:template match="root">
<xsl:if test="node">
<ul class="treeview filetree" id="{$ControlID}">
<xsl:apply-templates select="node" />
</ul>
<script type="text/javascript">
jQuery(function($) {
$("#<xsl:value-of select="$ControlID" />").treeview(
<xsl:value-of select="$Options" disable-output-escaping="yes" />
);
});
</script>
</xsl:if>
</xsl:template>
<xsl:template match="node">
<li>
<xsl:if test="node and (#depth != 0 or #breadcrumb = 1)">
<xsl:attribute name="class">open</xsl:attribute>
</xsl:if>
<xsl:choose>
<xsl:when test="#enabled = 0">
<xsl:value-of select="#text" />
</xsl:when>
<xsl:otherwise>
<a href="{#url}">
<xsl:choose>
<xsl:when test="#selected=1">
<xsl:attribute name="class">selected breadcrumb</xsl:attribute>
</xsl:when>
<xsl:when test="#breadcrumb=1">
<xsl:attribute name="class">breadcrumb</xsl:attribute>
</xsl:when>
</xsl:choose>
<xsl:value-of select="#text" />
</a>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="node">
<ul style="list-item-style:none">
<xsl:apply-templates select="node" />
</ul>
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>
It would help if you supplied an example of the input code you would like to transform.
I assume its basically something like this:
<root>
<node enabled="1" depth="1" text="Service" selected="true" breadcrumb="0"/>
<node>
<node>
<node/>
</node>
</node>
<node>
<node/>
</node>
<node/>
</root>
You can skip the first template-match and those first if-element and directly match only what you're interested in. Without testing, something like this should do the trick:
<!-- ... -->
<!-- process only "root" elements that have at least one "node" element -->
<xsl:template match="/root[node]">
<ul class="treeview filetree" id="{$ControlID}">
<xsl:apply-templates select="node" />
</ul>
<!-- ... -->
</xsl:template>
<xsl:template match="node">
<!-- ... -->
</xsl:template>
Without the source XML it's really hard to work out what you're trying to do here, but I'd say the main reason you're getting all nodes is the the template to match the node element is recursive and does not hide the descendants. If you add display:none to the style attribute on the ul element at the end of the node template (or change list-item-style to display), you may get what you want.
If you're only getting root items, you'll want to change the NodeSelector defined for the menu. I believe that the shorthand value RootChildren will give you what you want.
I have a XHTML Template. I want to convert dynamically the xhtml file to xslt . Waht is the best way to do this?
Below written the XHTML content
<div>
{:header:}
<br />
{:date:}
<p>
{:mailingattn:} <br />
{:facilityname:} <br />
{:facilitystreet:} <br />
{:facilitystreet2:} <br />
{:facilitycity:}, {:facilitystate:} {:facilityzip:} <br />
{:facilitycountry:}
</p>
<p>
{:message:}
</p>
<p>
Sincerely,<br />
{:signature:}
</p>
{:footer:}
</div>
I don't know precisely the semantics of the macros used in your XHTML file, but the following transformation produces an XSLT stylesheet which is hopefully equivalent, and if not, should be easily tailored to your needs:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:element name="xsl:stylesheet">
<xsl:attribute name="version" select="'2.0'"/>
<xsl:element name="xsl:template">
<xsl:attribute name="match" select="'/*'"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:variable name="regex">\{:([a-zA-Z0-9]*):\}</xsl:variable>
<xsl:analyze-string select="." regex="{$regex}">
<xsl:matching-substring>
<xsl:element name="xsl:value-of">
<xsl:attribute name="select" select="regex-group(1)"/>
</xsl:element>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
Did you consider using XProc and <p:template> ?
http://www.w3.org/TR/xproc-template/#c.template
how can I display the text inside a for-each loop, in xslt, using each time a different css class? can I generate somehow the name of the css class based on some variable or something?? is it possible?
for example in the following code inside the foreach loop can I display the li with a different css class each time...such as in the first iteration li class="1"...second iteartion li class="2"...and obvious in css exists li.1{..}, li.2{...}
<xsl:output method="text" indent="no"/>
<xsl:variable name="newline">
<xsl:text>
</xsl:text>
</xsl:variable>
<xsl:template match="/">
<xsl:text>Collections of books</xsl:text>
<xsl:value-of select="$newline"/>
<xsl:variable name="index" select="0"/>
<xsl:for-each select="collection/book">
<xsl:sort select="category"/>
<xsl:variable name="lastCat" select="category"/>
<xsl:if test="not(preceding-sibling::book[category=$lastCat])">
<xsl:value-of select="$newline"/>
<xsl:value-of select="category"/>
<xsl:value-of select="$newline"/>
<xsl:value-of select="$newline"/>
</xsl:if>
<ul>
<li>
<xsl:value-of select="title"/>
<xsl:text> </xsl:text>
<xsl:value-of select="author"/>
<xsl:text> </xsl:text>
<xsl:value-of select="year"/>
<xsl:text> </xsl:text>
<xsl:value-of select="isbn"/>
</li>
</ul>
<xsl:value-of select="$newline"/>
</xsl:for-each>
</xsl:template>
You're XSLT is a little confusing as to whether you are outputting text or html. However, in answer to your immediate question you can easily add attributes to the exist li elements you are outputing in one of two ways.
Firstly, you can make use of the xsl:attribute statement, which should be added immediately following the li tag.
<li>
<xsl:attribute name="class">
<xsl:value-of select="position()" />
</xsl:attribute>
...
The better method though is to use 'attribute value templates' to specify attributes. (The curly brackets indicate an AVT to be evaluated in this case, as opposed to being a piece of text to be literally output)
<li class="{position()}">
...
In both cases the output will be similar to the following
<li class="1">...
<li class="2">...
In this example I am simply using the position() to demonstrate how you can set attributes, but you could easily use variables if required.
<li class="{$classname}">
To start:
<test style="font:2px;color:#FFFFFF" bgcolor="#CCCCCC" TOPMARGIN="5">style</test>
Using XSLT/XPATH, I copy everything over from my document
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
But I'm not sure how to get this result using XSLT/XPATH:
<test style="background-color:#CCCCCC; margin-top:1;font:2px;color:#FFFFFF">style</test>
I think I'm failing at the XPATH. This is my attempt at just retrieving bgColor:
<xsl:template match="#bgColor">
<xsl:attribute name="style">
<xsl:text>background-color:</xsl:text>
<xsl:value-of select="."/>
<xsl:text>;</xsl:text>
<xsl:value-of select="../#style"/>
</xsl:attribute>
</xsl:template>
Unfortunately, even this breaks when style is placed after bgColor in the original document. How can I append these deprecated attribute values into one inline style attribute?
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<test style="{#style};background-color:{#bgcolor};margin-top:{#TOPMARGIN}">
<xsl:value-of select="."/>
</test>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<test style="font:2px;color:#FFFFFF"
bgcolor="#CCCCCC" TOPMARGIN="5">style</test>
produces the wanted, correct result:
<test style="font:2px;color:#FFFFFF;background-color:#CCCCCC;margin-top:5">style</test>
Explanation: Use of AVT.
May be not the best way, but it works:
<xsl:template match="test">
<xsl:element name="{name()}">
<xsl:apply-templates select="#*[name() != 'bgcolor']"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:copy/>
</xsl:template>
<xsl:template match="#style">
<xsl:attribute name="style">
<xsl:value-of select="."/>
<xsl:text>;background-color:</xsl:text>
<xsl:value-of select="../#bgcolor"/>
</xsl:attribute>
</xsl:template>
I'm trying to write some xsl to style an RSS feed. I need to trim the first 10 characters off the title of each item.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/rss">
<ul>
<xsl:for-each select="channel/item">
<li><strong><xsl:value-of select="title"/>
</strong>
More</li>
</xsl:for-each>
</ul>
</xsl:template>
<xsl:template name="trimtitle">
<xsl:param name="string" select="." />
<xsl:if test="$string">
<xsl:text>Foo</xsl:text>
<xsl:call-template name="trimtitle">
<xsl:with-param name="string" select="substring($string, 10)" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="title">
<xsl:call-template name="title" />
<xsl:value-of select="." />
</xsl:template>
</xsl:stylesheet>
I think you should write your substring function as this:
substring($string,1, 10)
Look at here
http://www.zvon.org/xxl/XSLTreference/Output/function_substring.html
What are you doing in your trimtitle template?
Why are you calling trimtitle recursive..?
The easiest way to show a trimmed string is with:
<xsl:value-of select="substring(title,0,10)"/>