How can one match a subnode in xslt in xml? - html

I've inherited a project that wants to use xslt to transform some html. Matching works with '/', but I can't get it to run on a subnode
I've found some code snippet on mozilla, that applies xslt transformation to html on mozilla, the code works https://developer.mozilla.org/en-US/docs/Web/XSLT/XSLT_JS_interface_in_Gecko/Advanced_Example.
The Problem is that I'm not able to template match the node "firmenliste"
What I use is:
var xslRef;
var xslloaded = false;
var xsltProcessor = new XSLTProcessor();
var myDOM;
var xmlRef = document.implementation.createDocument("", "", null);
p = new XMLHttpRequest();
p.open("GET", "xsl/FirmenListe.xsl",false);
p.send(null);
xslRef = p.responseXML;
xsltProcessor.importStylesheet(xslRef);
xmlRef = document.implementation.createDocument("", "", null);
// we want to move a part of the DOM from an HTML document to an XML document.
// importNode is used to clone the nodes we want to process via XSLT - true makes it do a deep clone
var myNode = document.getElementById("example");
var clonedNode = xmlRef.importNode(myNode, true);
// after cloning, we append
xmlRef.appendChild(clonedNode);
var fragment = xsltProcessor.transformToFragment(xmlRef, document);
// clear the contents
document.getElementById("example").innerHTML = "";
myDOM = fragment;
// add the new content from the transformation
document.getElementById("example").appendChild(fragment)
The corresponding html and xslt looks like:
<xml id="Data">
<data id="example" xmlns:dt="urn:schemas-microsoft-com:datatypes">
<firmenliste></firmenliste>
</data>
</xml>
<?xml version ='1.0'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="/">
b
<xsl:apply-templates select="firmenliste"/>
</xsl:template>
<xsl:template match="firmenliste">
A
</xsl:template>
</xsl:stylesheet>
The output should be
<xml id="Data">
<data id="example" xmlns:dt="urn:schemas-microsoft-com:datatypes">
bA
</data>
</xml>
But what i get is
<xml id="Data">
<data id="example" xmlns:dt="urn:schemas-microsoft-com:datatypes">
b
</data>
</xml>
Edit: The problem is reproducible in https://next.plnkr.co/edit/Yvc59BPQmI1PHlSy?open=lib%2Fscript.js&preview

I think the main problem is that you start with elements in a HTML DOM document which since HTML5 are by definition in the XHTML namespace http://www.w3.org/1999/xhtml and then clone and copy them to an XML document where they keep their namespace but where in XSLT/XPath a path or match pattern like firmenliste selects or matches elements of that name in no namespace and not in the XHTML namespace.
So using
<xsl:template match="/">
b
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="xhtml:firmenliste" xmlns:xhtml="http://www.w3.org/1999/xhtml">
A
</xsl:template>
instead would fix that problem: https://next.plnkr.co/edit/tsB9qwCafLodg8Rz?open=lib%2Fscript.js&preview
But the whole approach of using non-defined elements like xml or firmenliste in HTML and moving between HTML DOM and XML DOMs is asking for trouble in my experience. Consider to keep the XML data you want to transform outside of the HTML document in a separate XML document, only use XSLT on XML documents and only use its transformation result to be inserted into an HTML DOM if you have used transformToFragment with the owning HTML document as the second argument.

In your xslt, while matching root node with '/', You need to give whole xPath to match <firmenliste> in <xsl:apply-templates>
Try the same by replacing the line <xsl:apply-templates select="firmenliste"/>
with <xsl:apply-templates select="/xml/data/firmenliste"/>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
b
<xsl:apply-templates select="/xml/data/firmenliste" />
</xsl:template>
<xsl:template match="firmenliste">
A
</xsl:template>
</xsl:stylesheet>

Related

Using XSLT to transform XML to JSON

I would like to use XSLT to transform some XML into JSON.
The XML looks like the following:
<DATA_DS>
<G_1>
<ORGANIZATION_NAME>My Company 1</ORGANIZATION_NAME>
<ORGANIZATIONID>901</ORGANIZATIONID>
<ITEMNUMBER>20001</ITEMNUMBER>
<ITEMDESCRIPTION>Item Description 1</ITEMDESCRIPTION>
</G_1>
<G_1>
<ORGANIZATION_NAME>My Company 1</ORGANIZATION_NAME>
<ORGANIZATIONID>901</ORGANIZATIONID>
<ITEMNUMBER>20002</ITEMNUMBER>
<ITEMDESCRIPTION>Item Description 2</ITEMDESCRIPTION>
</G_1>
<G_1>
<ORGANIZATION_NAME>My Company 1</ORGANIZATION_NAME>
<ORGANIZATIONID>901</ORGANIZATIONID>
<ITEMNUMBER>20003</ITEMNUMBER>
<ITEMDESCRIPTION>Item Description 3</ITEMDESCRIPTION>
</G_1>
</DATA_DS>
I expect the JSON to look like the following:
[
{
"Item_Number":"20001",
"Item_Description":"Item Description 1"
},
{
"Item_Number":"20002",
"Item_Description":"Item Description 2"
},
{
"Item_Number":"20003",
"Item_Description":"Item Description 3"
}
]
What is the recommended way to do this?
I am considering two approaches:
Try using the fn:xml-to-json function, as defined at https://www.w3.org/TR/xpath-functions-31/#func-xml-to-json. But as I understand, the input XML must follow a specific format defined at: https://www.w3.org/TR/xpath-functions-31/schema-for-json.xsd. And I also need the field names in the output JSON to be specifically "Item_Number" and "Item_Description".
Manually code the bracket and brace characters, "[", "]", "{", and "}", along with the field names "Item_Number" and "Item_Description". Then use a standard function to list the values and ensure that any special characters are handled properly. For example, the "&" character should appear normally in the JSON output.
What is the recommended way to do this, or is there a better way that I have not considered?
I would take the first approach, but start with transforming the given input to the XML format expected by the xml-to-json() function. This could be something like:
XSLT 3.0
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/2005/xpath-functions">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/G_1">
<!-- CONVERT INPUT TO XML FOR JSON -->
<xsl:variable name="xml">
<array>
<xsl:for-each-group select="*" group-starting-with="ORGANIZATION_NAME">
<map>
<string key="Item_Number">
<xsl:value-of select="current-group()[self::ITEMNUMBER]"/>
</string>
<string key="Item_Description">
<xsl:value-of select="current-group()[self::ITEMDESCRIPTION]"/>
</string>
</map>
</xsl:for-each-group>
</array>
</xsl:variable>
<!-- OUTPUT -->
<xsl:value-of select="xml-to-json($xml)"/>
</xsl:template>
</xsl:stylesheet>
Demo: https://xsltfiddle.liberty-development.net/bFWR5DQ
For simple mappings like that you can also directly construct XPath 3.1 arrays and maps i.e. in this case an array of maps:
<xsl:template match="DATA_DS">
<xsl:sequence select="array { G_1 ! map { 'Item_Number' : string(ITEMNUMBER), 'Item_Description' : string(ITEMDESCRIPTION) } }"/>
</xsl:template>
Then serialize as JSON with <xsl:output method="json" indent="yes"/>: https://xsltfiddle.liberty-development.net/ejivdGS
The main disadvantage is that maps have no order so you can't control the order of the items in a map, for instance for that example and the used Saxon version Item_Description is output before Item_Number.
But in general transforming to the format for xml-to-json provides more flexibility and also allows you to control the order as the function preserves the order in the XML representation of JSON.
This is the result of taking the solution posted by michael.hor257k and applying it to my revised input XML:
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/2005/xpath-functions">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/DATA_DS">
<!-- CONVERT INPUT TO XML FOR JSON -->
<xsl:variable name="xml">
<array>
<xsl:for-each select="G_1">
<map>
<string key="Item_Number">
<xsl:value-of select="ITEMNUMBER"/>
</string>
<string key="Item_Description">
<xsl:value-of select="ITEMDESCRIPTION"/>
</string>
</map>
</xsl:for-each>
</array>
</xsl:variable>
<!-- OUTPUT -->
<xsl:value-of select="xml-to-json($xml)"/>
</xsl:template>
</xsl:stylesheet>

Output of Perl destroys specific letters from non-English languages

I have an XML file as follows:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="test.xslt"?>
<results>
<test name="sentence1">
<description href="#ömr">
ömr1, ämr1, ümr1 and pär1
</description>
</test>
<test name="sentence2" href="#pär2">
<description>
ömr2, ämr2, ümr2 and pär2
</description>
</test>
<test name="sentence3" href="#pär3">
<description>
ömr3, ämr3, ümr3 and pär3
</description>
</test>
</results>
Then here is the XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:b="http://www.froglogic.com/XML2"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="html" version="5.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="Summary/test">
<html>
<body>
<xsl:for-each select="//test">
<xsl:variable name="linkMe" select="#name"/>
<xsl:value-of select="description"/>
<a href="#{$linkMe}" >
<xsl:value-of select="$linkMe" />
</a>
<xsl:value-of select="description"/>
</xsl:for-each>
</body>
</html>
</xsl:template>
I want to convert the XML to an HTML file using Perl. But it's going to have not desired output although I have told Perl I want output as a UTF-8.
The perl code is like this:
use strict;
use warnings;
use XML::LibXML;
use XML::Writer;
use XML::LibXSLT;
use XML::Parser;
use Encode qw( is_utf8 encode decode );
my $XML_File = "test2.xml";
my $XSLT_File = "test2.xslt";
my $HTML_File = "test2.html";
sub XML2HTML {
my $xml_parser = XML::LibXML->new('1.0', 'UTF-8');
my $xslt_parser = XML::LibXSLT->new('1.0', 'UTF-8');
my $xml = $xml_parser->parse_file($XML_File);
$xml->setEncoding('UTF-8');
my $xsl = $xml_parser->parse_file($XSLT_File);
my $stylesheet = $xslt_parser->parse_stylesheet($xsl);
my $results = $stylesheet->transform($xml);
my $output = $stylesheet->output_string($results);
$stylesheet->output_file($results, $HTML_File);
}
&XML2HTML($XML_File, $XSLT_File, $HTML_File);
Another question is how I could have UTF-8-BOM output as file? I searched the internet and could not find an exact answer. They all mention UTF-8 rather than UTF-8-BOM.
The HTML output seems unpleasant:
ömr1, ämr1, ümr1 and pär1 ömr2, ämr2, ümr2 and pär2 ömr3, ämr3, ümr3 and pär3
The encoding format in HTML is
Codepage 1252(Western)
and it is strange!
First, you have a subroutine which operates on global variables. That is not a good idea. Instead, those values as arguments to the function so your function is not tied to names you use in other places in your program.
Second, you do not do anything with $output, but storing the output in it will still increase the memory footprint of your program.
Third, looking at the underlying XS code for write_file, we see:
xsltSaveResultToFilename(filename, doc, self, 0);
And, xsltSaveResultToFilename is documented here. Looking at the source code for xsltSaveResultToFilename, we note that the routine deduces the output encoding from the stylesheet. So, the problem has to lie elsewhere.
It turns out, my initial diagnosis was incorrect. After getting my hands on a system with the necessary libraries, I ran your script (which revealed syntax errors in your XSL file -- don't post code we cannot run). After fixing those, I realized the code was producing UTF-8 encoded output, but the HTML did not include a declaration of document encoding. Therefore, when I viewed in my browser, it tried to use Windows 1252. Your XSL template needs to declare the encoding of the HTML document as well. Of course, if add the BOM, you probably don't need the declaration in the head of the document.
The following script seems to work for me:
use strict;
use warnings;
use autouse Carp => 'croak';
use File::BOM ();
use XML::LibXML;
use XML::LibXSLT;
xml_to_html('test.xml', 'test.xsl', 'test.html');
sub xml_to_html {
my ($xml_file, $xsl_file, $html_file) = #_;
open my $out, '>:unix', $html_file
or croak "Failed to open '$html_file': $!";
print $out $File::BOM::enc2bom{'UTF-8'}
or croak "Failed to write UTF-8 BOM: $!";
my $xslt_parser = XML::LibXSLT->new;
my $xml_parser = XML::LibXML->new;
my $xml = $xml_parser->parse_file( $xml_file );
my $xsl = $xml_parser->parse_file( $xsl_file );
my $style = $xslt_parser->parse_stylesheet( $xsl );
my $results = $style->transform( $xml );
$style->output_fh( $results, $out );
return;
}
with this template:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:b="http://www.froglogic.com/XML2"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="html" version="5.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>,
</head>
<body>
<xsl:for-each select="//test">
<xsl:variable name="linkMe" select="#name"/>
<xsl:value-of select="description"/>
<a href="#{$linkMe}" >
<xsl:value-of select="$linkMe" />
</a>
<xsl:value-of select="description"/>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
and produces the following output:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html xmlns:b="http://www.froglogic.com/XML2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">,
</head>
<body>
ömr1, ämr1, ümr1 and pär1
sentence1
ömr1, ämr1, ümr1 and pär1
ömr2, ämr2, ümr2 and pär2
sentence2
ömr2, ämr2, ümr2 and pär2
ömr3, ämr3, ümr3 and pär3
sentence3
ömr3, ämr3, ümr3 and pär3
</body>
</html>
I have
$ pacman -Ss libxslt
extra/libxslt 1.1.29+42+gac341cbd-1 [installed]
XML stylesheet transformation library
which does not seem to include support for generating HTML5 doctype.
Depending on your specific needs, you may have to tweak the XSLT file further.

very simple but i got lost, sorting alphabetically xml using xsl and visualize output in html

I'm creating something like a dictionary online where users can add their words using a simple form and then they can see all the words in a page, in alphabetical order of the words, the problem is that it doesn't show it alphabetically but in the order of insertion.
I got 3 files, res.xml, which is the file with the data, tt.xsl which is supposed to sort res.xml in alphabetical order, and index.html which shows the data to the user, i would like to sort the data contained in the xml file alphabetically using the attribute 'WORD', but when i run index.html it doesn't sort it , it just shows it in the order of reading data, what's the easiest way to make it work?
Here are the 3 files
THIS IS THE HTML FILE WITH A SCRIPT I'M USING TO VISUALIZE IT
<html>
<body>
<script>
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("GET","res.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
document.write("<table border='1'>");
var x=xmlDoc.getElementsByTagName("TERM");for (i=0;i<x.length;i++)
{
document.write("<tr><td>");
document.write(x[i].getElementsByTagName("WORD")[0].childNodes[0].nodeValue);
document.write("</td><td>");
document.write("</td><td>");
document.write(x[i].getElementsByTagName("LANGUAGE")[0].childNodes[0].nodeValue);
document.write("</td><td>");
document.write(x[i].getElementsByTagName("DESCRIPTION")[0].childNodes[0].nodeValue);
document.write("</td></td>");
document.write("</td><td>");
document.write(x[i].getElementsByTagName("EDITBY")[0].childNodes[0].nodeValue);
document.write("</td></tr>");
}
document.write("</table>");
</script>
</body>
</html>
//------------------------------
THIS IS THE XML FILE res.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<?xml-stylesheet type="text/xsl" href="tt.xsl"?>
<LIBRARY>
<TERM>
<EDITBY>giovanni</EDITBY>
<WORD>ciao</WORD>
<LANGUAGE>Italian</LANGUAGE>
<DESCRIPTION>
it means hi
</DESCRIPTION>
</TERM>
<TERM>
<EDITBY>giacomo</EDITBY>
<WORD>all</WORD>
<LANGUAGE>italian</LANGUAGE>
<DESCRIPTION>
significa tutto
</DESCRIPTION>
</TERM>
</LIBRARY>
//----------------------------------
THIS IS THE XSL tt.xsl
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="LIBRARY/TERM">
<xsl:sort select="WORD"/>
<tr>
<td><xsl:value-of select="LANGUAGE"/></td>
<td><xsl:value-of select="WORD"/></td>
</tr>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
RESULT, IT SHOWS THE DATA BUT NOT IN ALPHABETICAL ORDER
ANY HELP WOULD BE GREATLY APPRECIATED
Looking at the javascript, after getting the XML, you are performing commands such as getElementsByTagName("WORD"). WORD is an element in your input XML, but your XSLT would transform your XML input tr and td. This should give you a clue that your XSLT is doing actually being called, and your XML is not being transforming. All your javascript is doing is reading the elements from the XML in the order they appear.
Now, in your XML you do have this processing instruction:
<?xml-stylesheet type="text/xsl" href="tt.xsl"?>
But this 'instruction' is only going to be 'processed' if the XML was access directly in the browser (i.e you type "res.xml" into the URL to display the XML). Reading the XML file with javascript will not actually pay any attention to this processing instruction. You are simply reading the contents of the file at this point, and it is up to you to write code to act on it.
It is not too hard to write javascript to perform an XSLT transformation. The main fiddliness comes from IE doing it different to Firefox and Chrome. I did a quick search on Stack Overflow, and found this question which explains how to do it
http://stackoverflow.com/questions/5722410/how-can-i-use-javascript-to-transform-xml-xslt
Here is the adaptation of the javascript provided that should work in this case
<html>
<body>
<script>
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("GET","test.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
xmlhttp.open("GET","test.xslt",false);
xmlhttp.send();
xsl=xmlhttp.responseXML;
var result;
// IE method
if (window.ActiveXObject) {
result = new ActiveXObject("MSXML2.DOMDocument");
xmlDoc.transformNodeToObject(xsl, result);
// Other browsers
} else {
result = new XSLTProcessor();
result.importStylesheet(xsl);
result = result.transformToDocument(xmlDoc);
}
var x, ser, s = '';
// IE method.
if (result.childNodes[0] && result.childNodes[0].xml) {
for (x = 0; x < result.childNodes.length; x += 1) {
s += result.childNodes[x].xml;
}
// Other browsers
} else {
ser = new XMLSerializer();
for (x = 0; x < result.childNodes.length; x += 1) {
s += ser.serializeToString(result.childNodes[x]);
}
}
document.write(s);
</script>
</body>
</html>
The only thing to note here, is that it would be better to output the "table" element in your XSLT too. So, you XSLT would look something like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="html" />
<xsl:template match="/">
<table border="1">
<xsl:for-each select="LIBRARY/TERM">
<xsl:sort select="WORD"/>
<tr>
<td><xsl:value-of select="LANGUAGE"/></td>
<td><xsl:value-of select="WORD"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
You need to add an output element to your XSL like the below:
<xsl:output method="html"/>
That would go right below your stylesheet tag.
That is the minimum information that you can add to the output tag. Without it, some (all?) processors won't handle the XSL at all.
As a side note, I'm not entirely sure what you are doing with your javascript but you should be able to create your entire table structure within the XSL without all of the extra scripting. But that would be the topic of a second question.
So when you run this XML
<LIBRARY>
<TERM>
<EDITBY>giovanni</EDITBY>
<WORD>ciao</WORD>
<LANGUAGE>Italian</LANGUAGE>
<DESCRIPTION>
it means hi
</DESCRIPTION>
</TERM>
<TERM>
<EDITBY>giacomo</EDITBY>
<WORD>all</WORD>
<LANGUAGE>italian</LANGUAGE>
<DESCRIPTION>
significa tutto
</DESCRIPTION>
</TERM>
</LIBRARY>
With this XSL
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select="LIBRARY/TERM">
<xsl:sort select="WORD"/>
<tr>
<td><xsl:value-of select="LANGUAGE"/></td>
<td><xsl:value-of select="WORD"/></td>
</tr>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
You get this HTML, with the information sorted by the WORD element.
<tr>
<td>italian</td><td>all</td>
</tr><tr>
<td>Italian</td><td>ciao</td>
</tr>

How to get multiple views (html) using XSLT on 1 XML file

I have 1 big XML file with all data I need.
What I need is something like this:
1 page = Overview. on this page a table is shown. Each row starts with a hyperlink to a detail page.
I am looking for a way to do it with XML, XSLT and HTML only. No server side processing.
Any way to achieve this?
Right now the XML has the XSLT to use for the overview specified in the header:
<?xml-stylesheet type="text/xsl" href="overview.xslt"?>
If I cannot do it with multiple XSLT files, is there a way to read the querystring from the url in XSLT?
I see two options:
Let the XSLT create one big HTML and hide all the sections except the one that you'd like to show.
Use JavaScript to initiate different transforms, replacing the body of the current HTML with the body of the HTML that was returned by the transformation. You could either use one stylesheet that takes a different route depending on the value of a <xsl:parameter> that needs to be supplied for every transform, or you use different stylesheets.
Let's assume you have the following XML (lets call it text.xml):
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<myXml>
<chapter id="c1">
<heading>Heading 1</heading>
<content>This is text of chapter one.</content>
</chapter>
<chapter id="c2">
<heading>Heading 2</heading>
<content>This is text of chapter two.</content>
</chapter>
</myXml>
Then, for suggestion 1, you could do:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head>
<title></title>
<script type="text/javascript">
function showView(id) {
document.getElementById("dynamicStyle").innerHTML = "#" + id + "{display:inline}";
}
</script>
<style type="text/css">
.view {display:none};
</style>
<style type="text/css" id="dynamicStyle">
#overview{display:inline}
</style>
</head>
<body>
<div class="view overview" id="overview">
<h1>Overview</h1>
<xsl:apply-templates select="myXml/chapter" mode="overview"/>
</div>
<xsl:apply-templates select="myXml/chapter" mode="detail"/>
</body>
</html>
</xsl:template>
<xsl:template match="chapter" mode="overview">
<div><xsl:value-of select="heading"/></div>
</xsl:template>
<xsl:template match="chapter" mode="detail">
<div class="view detail" id="{#id}">
<div>Back to overview</div>
<xsl:apply-templates mode="detail"/>
</div>
</xsl:template>
<xsl:template match="heading" mode="detail">
<h1><xsl:value-of select="."/></h1>
</xsl:template>
<xsl:template match="content" mode="detail">
<div><xsl:value-of select="."/></div>
</xsl:template>
</xsl:stylesheet>
The key is creating separate divs for each view and toggling between them by letting JavaScript change the CSS.
Method 2 might look like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:param name="view" select="'overview'"/>
<xsl:template match="/">
<html>
<head>
<title></title>
<script type="text/javascript">
function showView(id) {
document.documentElement.replaceChild(transform(id).body, document.body);
}
function loadXML(fileName,mime) {
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open("GET",fileName,false);
if(mime) xmlHttpRequest.overrideMimeType(mime);
xmlHttpRequest.send("");
return xmlHttpRequest.responseXML;
}
function transform(view) {
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(loadXML('test.xsl','application/xslt+xml'));
xsltProcessor.setParameter(null,'view',view);
return xsltProcessor.transformToDocument(loadXML('test.xml'),document);
}
</script>
</head>
<body>
<xsl:choose>
<xsl:when test="$view = 'overview'">
<div>
<h1>Overview</h1>
<xsl:apply-templates select="myXml/chapter" mode="overview"/>
</div>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="myXml/chapter[#id = $view]" mode="detail"/>
</xsl:otherwise>
</xsl:choose>
</body>
</html>
</xsl:template>
<xsl:template match="chapter" mode="overview">
<div><xsl:value-of select="heading"/></div>
</xsl:template>
<xsl:template match="chapter" mode="detail">
<div>
<div>Back to overview</div>
<xsl:apply-templates mode="detail"/>
</div>
</xsl:template>
<xsl:template match="heading" mode="detail">
<h1><xsl:value-of select="."/></h1>
</xsl:template>
<xsl:template match="content" mode="detail">
<div><xsl:value-of select="."/></div>
</xsl:template>
</xsl:stylesheet>
The key here is loading the stylesheet and the XML using JavaScript and using the JavaScript object XSLTProcessor to do a transform, then replace the body of the document. In this example, I use one stylesheet with different parameters, but you could also load different stylesheet. You'd have to adjust the transform() function accordingly, replacing test.xsl with a variable that needs to be supplied somehow.

&#160 shows up as a '?' question mark on HTML

i have a problem with XSLT...
<xsl:text> </xsl:text>
Then after generation, for some reason the resulting JSP file produces a '?' instead. What's wrong?
My recent system changes:
I changed Java5 -> Java6
Weblogic -> Weblogic12
Eclipse Ganymede -> Oracle Pack Eclipse
EDIT 1: <xsl:output method="xml"/>, encoding=UTF-8
The original XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:include href="common.xsl"/>
<xsl:output method="xml"/>
...
<xsl:template name="makeLink">
<xsl:variable name="fieldtype" select="name()"/>
<xsl:variable name="currentNode"><xsl:value-of select="generate-id()"/></xsl:variable>
<xsl:variable name="appendSpace">
<xsl:for-each select="ancestor::ButtonList[position() = 1]/descendant::Button">
<xsl:if test="generate-id() = $currentNode and position() > 1">true</xsl:if>
</xsl:for-each>
</xsl:variable>
<a href="{$url}">
<xsl:attribute name="id">btn_<xsl:value-of select="Action"/></xsl:attribute>
<xsl:call-template name="populateAttributes">
<xsl:with-param name="fieldtype">
<xsl:value-of select="$fieldtype"/>
</xsl:with-param>
</xsl:call-template>
<xsl:copy-of select="#class"/>
<xsl:copy-of select="#style"/>
<xsl:text><span><span></xsl:text><xsl:value-of select="$buffer"/><xsl:text></span></span></xsl:text>
</a>
<xsl:if test="not(#omitWhiteSpace)">
<xsl:text> </xsl:text>
</xsl:if>
<xsl:if test="ReadOnly and ReadOnly != 'someReadOnlyMethod'
and ReadOnly != 'someReadyOnlyMethod'
and ReadOnly != ''">
<xsl:text></c:if></xsl:text>
</xsl:if>
</xsl:template>
....
Transformed (after XSLT), and resulting JSP page:
<%# page contentType = "text/html;charset=GBK"%>
<%# page isELIgnored = "false"%>
<%# page language="java"
import=" my.controller.*, my.core.config.*, my.core.datastructure.*, my.core.error.*, my.core.util.*,
my.service.Constants, my.service.modulesvr.ModuleBean, myW.sn.*, java.util.Locale, java.util.Map"%>
<%# taglib uri="http://www.mycompany.com/my/tags/htmltag-10" prefix="html"%>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%MySn mySession = (MySn) session.getValue("MySn"); QuickSearchController mb = (mySession == null) ? null : (QuickSearchController)
mySession.getModuleBean(); String sessionToken = mySession.getSessionToken(); String htmlCharSet = mySession.getEncoding();
MyUsr user = mySession.getMyUsr(); String[] result; Object o;%>
.........
<span><span>NEW PROP</span></span> </c:if>
EDIT 2: it seems like if i use <xsl:text>&#160;</xsl:text> instead of <xsl:text> </xsl:text>...the problem seems to have gone away. In the JSP, it will appear as &#160 and on the browser, it is seen as a no-break space, which is expected.
That often happens if your encoding is wrong. What encoding are you writing your output in? How are you serving up the page? Possibly you are serializing in UTF-8 but trying to display in ISO-8859-1 (or Windows-1252), or vice-versa.
Check to see if the default encoding somewhere has changed.
Just because you say <xsl:output method="xml" encoding="UTF-8"/> doesn't mean that the program will honor it. Is the XSLT embedded in a piece of Java? Does the Java control the streams/readers/writers?
If you can save a portion of the file and dump it in HEX, you should quickly be able to find out. If you see 0xC2 0xA0 then your file is indeed in UTF-8. However, if you just see 0xA0 alone, then you are in ISO-8859-1 or one of its close relations.
It's also possible that the page is being rendered properly, but the page is being served up with the wrong encoding. Can you look at the headers returned, perhaps by using Firebug in Firefox or in Chrome "Web Developer->Information->View Response Headers" or by using the IE debug tools.