I have invalid HTML that I am trying to transform into valid HTML using an XSLT transformation. For example, I want to turn some attributes into inline CSS. Consider the following:
<table border="1" id="t01" width="100%">
.
.
.
</table>
The border and width attributes on the table element are obsolete. So I want to use inline CSS instead, like this:
<table style="border:1;width:100%;" id="t01">
.
.
.
</table>
How can I do this with XSLT?
You could certainly make this prettier, more generic, etc., but my first pass would be something like:
<xsl:template match="table">
<table>
<xsl:attribute name="style">
<xsl:if test="#border">
<xsl:value-of select="concat('border:', #border, '; ')"/>
</xsl:if>
<xsl:if test="#width">
<xsl:value-of select="concat('width:', #width, '; ')"/>
</xsl:if>
</xsl:attribute>
<xsl:copy-of select="#id"/>
<!-- either this or apply-templates -->
<xsl:copy-of select="*"/>
</table>
</xsl:template>
Related
I need to loop over a group of values and print them into a two-column table.
I thought about following solution (must be xslt1)
<table class="main">
<xsl:for-each select="Attribute/Gruppe">
<xsl:if test="current()/#ID=20064490">
<xsl:variable name="open_row"><![CDATA[<tr><td style="width:50%;">
<xsl:value-of select="current()/#name" /></td>]]></xsl:variable>
<xsl:variable name="closing_row"><![CDATA[<td style="width:50%;">
<xsl:value-of select="current()/#name" /></td></tr>]]></xsl:variable>
<xsl:variable name="table">
<xsl:for-each select="*">
<xsl:choose>
<xsl:when test="(position() mod 2) = 1">
<xsl:value-of select="$open_row"
disable-output-escaping="yes" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$closing_row"
disable-output-escaping="yes" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="$table"
disable-output-escaping="yes" />
</xsl:if>
</xsl:for-each>
</table>
I modified the code now I am using position() to find out if an closing <tr> or an opening </tr> is required.
The whole problem might be summarized to that you can not write single tags in XSLT. And that disable-output-escaping is not working.
Resulting HTML should be https://jsfiddle.net/dwetctm6/
For all the Nodes in the group. The order of the nodes in the table does not matter.
Furthermore assume following XML:
<bgroup>
<NODE1>text</NODE1>
<NODE2>text</NODE2>
<NODE n-1>text</NODE n-1>
<NODE n>text</NODE n>
</bgroup>
Dividing nodes into a two-column table is pretty trivial - especially if the order (across-first or down-first) does not matter.
Consider the following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/bgroup">
<table border="1">
<xsl:for-each select="*[position() mod 2 = 1]">
<tr>
<td><xsl:value-of select="."/></td>
<td><xsl:value-of select="following-sibling::*[1]"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Applied to the following well-formed inpout:
XML
<bgroup>
<NODE1>A</NODE1>
<NODE2>B</NODE2>
<NODE3>C</NODE3>
<NODE4>D</NODE4>
<NODE5>E</NODE5>
</bgroup>
the result will be:
<table border="1">
<tr>
<td>A</td>
<td>B</td>
</tr>
<tr>
<td>C</td>
<td>D</td>
</tr>
<tr>
<td>E</td>
<td/>
</tr>
</table>
rendered as:
Well, a few remarks must be made first of all:
You must learn how XSLT works: It is a language for templating that, being based on XML, must be composed in an ordered way: Every node open must be closed within the same scope. michael.hor257k is right: You shall not open a node within an scope and close it in another different one.
Also, there must be said that your input XML does not seem to be properly structured: It should be a repetition of nodes with same names and types. If all the NODE-n nodes are functionally or semantically equal, they should have the same name, no problem: You can have several nodes with the same name.
And now, the solution: Asuming your XML is as you have posted it, and since there can't be a templating definition based upon named nodes with different names, I have matched all the child nodes of the bgroup root node. So, the XSL can be as easy as this:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/bgroup">
<html>
<body>
<table>
<xsl:apply-templates select="*[position() mod 2 =1]"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="*">
<tr>
<td><xsl:value-of select="."/></td>
<td><xsl:value-of select="following-sibling::*[1]"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
It's important that you realise how, thanks to the ordered structure of the XSL language, you can see how the output will be with just a glimpse of the XSL template.
I am trying to parse a xml to generate a html report.The problematic section of the xml is as given
<failure message="Management changes link count is not 3$HiHello" type="junit.framework.AssertionFailedError">junit.framework.AssertionFailedError: Management changes link count is not 3$HiHelloJI
at CustomProjects.CommonTemplates.verifyManagementChanges(Unknown Source)
at CustomProjects.EmersonTest.testEmerson_VerifyManagementChanges(Unknown Source)
</failure>
The xslt written for parsing this is :
<xsl:choose>
<xsl:when test="failure">
<td>Failure</td>
<td><xsl:apply-templates select="failure"/></td>
<td>screenshot</td>
<td><xsl:apply-templates select="failurelink"/></td>
</xsl:when>
</xsl:choose>
<xsl:template match="failure">
<xsl:call-template name="display-failures"/>
</xsl:template>
<xsl:template match="failurelink">
<xsl:call-template name="display-failures-link"/>
</xsl:template>
<xsl:template name="display-failures">
<xsl:param name="FailText" select="#message"/>
<xsl:choose>
<xsl:when test="not(#message)">N/A</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-before($FailText,'$')"/>
</xsl:otherwise>
</xsl:choose>
<!-- display the stacktrace -->
<code>
<br/><br/>
<xsl:call-template name="br-replace">
<xsl:with-param name="word" select="."/>
</xsl:call-template>
</code>
<!-- the later is better but might be problematic for non-21" monitors... -->
<!--pre><xsl:value-of select="."/></pre-->
</xsl:template>
<xsl:template name="display-failures-link">
<xsl:param name="linktext" select="#message"/>
<xsl:choose>
<xsl:when test="not(#message)">N/A</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-after($linktext,'$')"/>
</xsl:otherwise>
</xsl:choose>
<!-- display the stacktrace -->
<code>
<br/><br/>
<xsl:call-template name="br-replace">
<xsl:with-param name="word" select="."/>
</xsl:call-template>
</code>
<!-- the later is better but might be problematic for non-21" monitors... -->
<!--pre><xsl:value-of select="."/></pre-->
</xsl:template>
Here I am getting the desired result(The String before $ sign) from display-failures template but on calling display-failures-link I am getting nothing.(Should get the string after $ sign).I dont know whether the problem is with sunstring function or with something else.Kindly let me know what I am doing wrong here.
Any help is highly appreciated.
The problem here is that you are trying to apply-templates on the XPath failurelink, but you don't have an element called <failurelink>, so this apply-templates isn't finding anything.
<xsl:apply-templates select="failurelink"/>
One way to apply two different templates on the same kind of element is to use modes:
<xsl:template match="failure">
<xsl:call-template name="display-failures"/>
</xsl:template>
<xsl:template match="failure" mode="link">
<xsl:call-template name="display-failures-link"/>
</xsl:template>
Then the area where you apply the templates would change to this:
<td>Failure</td>
<td><xsl:apply-templates select="failure"/></td>
<td>screenshot</td>
<td><xsl:apply-templates select="failure" mode="link"/></td>
But in your case, there's an even better approach. Just eliminate the second template, and do this:
Replace the whole <xsl:choose> with:
<xsl:apply-templates select="failure" />
Replace the first template you listed with:
<xsl:template match="failure">
<td>Failure</td>
<td><xsl:call-template name="display-failures"/></td>
<td>screenshot</td>
<td><xsl:call-template name="display-failures-link"/></td>
</xsl:template>
And delete the second template you listed.
I am currently running into some trouble with an XML/XSLT to HTML conversion I am working on.
In short, I want to use <br /> tags within an XML tag, so that the HTML file after transformation will display a line break. After some tries I got that to work, however at the cost of some other functionality. Namely the ability to highlight parts.
First the dumped down XML file. So basically, there are several possible tags which all contain a first and last name. In this case I want the first and last name to be parsed on a separate line (hence the <br /> tag). Furthermore in some instances a first or last name will need to be highlighted. In this case on line 3, last name "The Hand".
<swift_native>
<tag tag_code=":1:"><![CDATA[Jaco<br />Ronnie]]></tag>
<tag tag_code=":2:"><![CDATA[John<br />Doe]]></tag>
<tag tag_code=":2:"><![CDATA[Robbie<br />]]><highlight>The Hand</highlight></tag>
</swift_native>
So far, depending on the method I use within the XSLT I either am able to get the linebreaks correct or the highlighting. But not both: The following figure shows this.
Below you see the used XSLT file. Where you can see that using <xsl:apply-templates/> will make the highlighting work and <xsl:value-of select="." disable-output-escaping="yes"/> will let me use the <br /> correctly.
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- HTML Layout definition -->
<xsl:output method="html"/>
<xsl:template match="swift_native">
<html>
<head>
<title>
<xsl:apply-templates select="message_id"/>
</title>
<style type="text/css">
#tbl1,#tbl2 {display:none;}
#lnk1,#lnk2 {border:none;background:none;width:85px;}
td {FONT-SIZE: 75%; MARGIN: 0px; COLOR: #000000;}
td {FONT-FAMILY: verdana,helvetica,arial,sans-serif}
a {TEXT-DECORATION: none;}
table.subtable {border-collapse:collapse;}
table.subtable td {border:1px solid black;}
</style>
</head>
<body>
<table cellpadding="3" width="100%" class="subtable">
<tr bgcolor="#cccccc">
<td colspan="3">Block4</td>
</tr>
<xsl:apply-templates select="tag" />
</table>
</body>
</html>
</xsl:template>
<!-- Variable definition -->
<xsl:template match="tag">
<tr>
<td>
<b>
<xsl:value-of select="#tag_code" />
</b>
</td>
<td>
<xsl:value-of select="." disable-output-escaping="yes"/>
</td>
<td>
<xsl:apply-templates/>
</td>
</tr>
</xsl:template>
<xsl:template match="highlight">
<span style="background-color:yellow;">
<xsl:apply-templates/>
</span>
</xsl:template>
</xsl:stylesheet>
Obviously, the question is: does someone know a way in which I can use both the <br /> tag and the highlighting?
CDATA is telling the processor to interpret the contents as plain text, not markup. That's why disable-output-escaping is needed to prevent the <br/> from showing up as <br/>.
If you want to take advantage of disable-output-escaping, you'll have to break up the way that you select against the tag content.
Add a template
<xsl:template match="tag/text()">
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:template>
and change the value-of line to
<xsl:apply-templates select="text()|*"/>
One solution here is to use both:
<xsl:template match="tag">
<tr>
<td>
<b>
<xsl:value-of select="#tag_code" />
</b>
</td>
<td>
<xsl:apply-templates/>
</td>
</tr>
</xsl:template>
<xsl:template match="tag//text()">
<xsl:value-of select="." disable-output-escaping="yes" />
</xsl:template>
<xsl:template match="highlight">
<span style="background-color:yellow;">
<xsl:apply-templates />
</span>
</xsl:template>
Note however that if you do this, you need to ensure that any text values within the <tag> nodes are properly escaped in the CDATA, and doubly escaped outside it, that is, rather than
<tag tag_code=":2:"><![CDATA[Robbie & Bobbie <br />]]><highlight> & The Hand</highlight></tag>
You would need to have:
<tag tag_code=":2:"><![CDATA[Robbie & Bobbie<br />]]><highlight> & The Hand</highlight></tag>
So this is probably not a great approach if the <tag> elements have any possibility of containing XML special characters.
If you can ensure that the text directly below the <tag> will always be in a CDATA and that anything in lower nodes (e.g. <highlight>s) won't, then it's mildly simpler. You can replace the text matching template above with this one:
<xsl:template match="tag/text()">
<xsl:value-of select="." disable-output-escaping="yes" />
</xsl:template>
And then you just need to ensure that the stuff in the CDATA is properly escaped, and that anything else is just valid XML.
Finally, if you have some control over your source data, you should consider abandoning the CDATA and just having the <br /> right in the <tag>:
<tag tag_code=":2:">Robbie<br /><highlight>The Hand</highlight></tag>
Then you could use this XSL, which is much more robust than anything that uses disable-output-escaping:
<xsl:template match="tag">
<tr>
<td>
<b>
<xsl:value-of select="#tag_code" />
</b>
</td>
<td>
<xsl:apply-templates/>
</td>
</tr>
</xsl:template>
<xsl:template match="tag/#* | tag/node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="highlight">
<span style="background-color:yellow;">
<xsl:apply-templates />
</span>
</xsl:template>
Happily, there's a simple solution too. Just add this line to your xsl file:
<xsl:template match="br"><br/></xsl:template>
This way, there's no need to wrap the data into CDATA, but instead use the much more intuitive:
<tag tag_code=":1:">Jaco<br/>Ronnie</tag>
Likewise other common simple HTML tags may be included. Here's an example linking bold, italic, etc. to CSS styles, but a one-liner for each (like above) does work too:
<xsl:template match="i|b|u|strong">
<span>
<xsl:attribute name="class">html_<xsl:value-of select="name(.)" /></xsl:attribute>
<xsl:apply-templates />
</span>
</xsl:template>
If you find yourself doing this often, copy them all to html.xsl and use xsl:include to use them when needed.
if you want to make in html is
all wath you need is to make in XLS :
<br></br>
I am using xslt to transform an xml document to html for use in an email. I need to compare xml elements with another xml element value so that I know what format to give the value. Basically I have an xml structure as such:
<main>
<comparer>1</comparer>
<items>
<item>
<name>blarg</name>
<values>
<value>1</value>
<value>2</value>
</values>
</items>
</main>
The item information is being used to build a table:
<table>
<tr>
<td>blarg</td>
<td>1</td>
<td>2</td>
</tr>
</table>
What I need to be able to do is use xsl to compare the item values with the 'comparer' node value and if they are equal then bold the cell in the table otherwise the cell value i snot bolded. I need to accomplish this without the use of javascript so it has to be done in xsl. Right now, I am looking at using a xsl:variable then attempting to use the xsl:when to do the compare. Unfortunately, I am having little luck. This is what I have just started playing with for each row in the table:
<xsl:variable name="compare" select="//main/comparer" />
...
<xsl:for-each select="value">
<td>
<xsl:choose>
<xsl:when test=". = $compare">
<b>
<xsl:value-of select="."/>
</b>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>.
</xsl:otherwise>
</xsl:choose>
</td>
</xsl:for-each>
*Note: I left out most of the xsl for brevity. I'm just trying to focus on my issue.
I figured it out after some trial and error. Alejandro's answer appears that it would work, but I do not have the luxury of restructuring the xsl to make use of templating. Here is what I used to solve my issue:
<xsl:variable name="compare" select="//main/comparer" />
...
<xsl:for-each select="value">
<td>
<xsl:choose>
<xsl:when test="contains(., $expireDate)">
<b>
<xsl:value-of select="."/>
</b>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>.
</xsl:otherwise>
</xsl:choose>
</td>
</xsl:for-each>
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="main">
<table>
<xsl:apply-templates select="items"/>
</table>
</xsl:template>
<xsl:template match="item">
<tr>
<xsl:apply-templates/>
</tr>
</xsl:template>
<xsl:template match="name|value">
<td>
<xsl:apply-templates/>
</td>
</xsl:template>
<xsl:template match="value/text()[.=/main/comparer]">
<b>
<xsl:value-of select="."/>
</b>
</xsl:template>
</xsl:stylesheet>
Output:
<table>
<tr>
<td>blarg</td>
<td>
<b>1</b>
</td>
<td>2</td>
</tr>
</table>
Note: Pattern matching and node set comparison.
In any language, XSLT, PHP, Ruby, Perl, anything: how can I get the XPath or other path or identifier to the element containing the largest number of <p> tags?
This answer supposes that the html input is a well-formed XML document (such as an XHtml document).
In XSLT 1.0:
Given the following XML
<html>
<div id='A1'>
<p/>
<p/>
</div>
<div id='A2'>
<p/>
<p/>
<p/>
<p/>
</div>
<div id='A3'>
<p/>
<p/>
<p/>
</div>
</html>
This transformation produces the element that has the largest number of p children:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:key name="kElByP" match="*" use="boolean(p)"/>
<xsl:template match="/*">
<xsl:for-each select="key('kElByP', 'true')">
<xsl:sort data-type="number" order="descending"
select="count(p)"/>
<xsl:if test="position() = 1">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When the above transformation is applied on the XML document the correct result is produced:
<div id="A2">
<p />
<p />
<p />
<p />
</div>
Using XPath 2.0:
//*[count(p) = max(//*/count(p))]
You could use getElementsByTagName to find your <p>s and loop through the results to find the greatest value of childNodes.length with Javascript (assuming you have the option to do this on the client side).
The answer depends on the language. JavaScript for instance can naturally handle viewing the HTML DOM and there are libraries such as ProtoType that would make quick work of it. Similarly, Garann's answer would get you on your way.
In other languages, I would suggest using regular expressions to find tags that have paragraph tags in them. This would likely involve turning the entire html into a single string and processing it.
Assuming that it is properly formatted HTML, you could strip out all the content, leaving only the tags. Doing that first would make the subsequent steps move faster. Then it is likely a matter of some loops and some counters.
A very brute force solution in Perl, using XML::Twig:
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
my $max=0; # max number of p's
my $path; # path to the element
XML::Twig->new( twig_handlers =>
{ # _all_ is called for every single element in the XML
_all_ => sub { # $_ is the element
my $nb_p= $_->children( 'p');
if( $nb_p > $max)
{ $max= $nb_p;
$path= $_->xpath;
}
}
},
)
->parsefile( $ARGV[0]);
print "$path ($max p)\n";
After attempting to construct this is a recursive design... I saw the simple 2.0 solution. Oh well. An alternate solution, anyway.
<xsl:template match="/">
<xsl:variable name="MaxElements">
<xsl:call-template name="MaxElements">
<xsl:with-param name="elements" select="//div[p]"/>
<xsl:with-param name="largestSoFar" select="0"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$MaxElements"/>
</xsl:template>
<xsl:template name="MaxElements">
<xsl:param name="elements"/>
<xsl:param name="largestSoFar"/>
<xsl:choose>
<xsl:when test="$elements">
<xsl:variable name="CurrentNumber" select="count($elements[1]/p)"/>
<xsl:variable name="LargerNumber" select="if ($CurrentNumber > $largestSoFar) then ($CurrentNumber) else $largestSoFar"/>
<xsl:call-template name="MaxElements">
<xsl:with-param name="elements" select="$elements[position()>1]"/>
<xsl:with-param name="largestSoFar" select="$LargerNumber"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$largestSoFar"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>