I am trying to generate output like this through XSL:
<script>
var quickCatalogueCategories = {
for each values do {
'{title}': 'http://www.news.com/{search key}',
}
};
</script>
The problem is I am getting the output but after each title I should get url instead after all titles I am getting the URL. Even after the URL I should have only one key but continuously displaying all four keys.
Can you tell me how to fix it?
Trying to achieve output like this:
<script>
var quickCatalogueCategories = {
'{What's New}':'http: //www.news.com/{news}',
'{Featured}':'http: //www.news.com/{featured}',
'{Most Delivered}':'http: //www.news.com/{most-delivered}',
'{Online Only}':'http: //www.news.com/{online-only}',
}
</script>
try this code
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Skin: Default XSL -->
<!--xsl:include href="http://www.interwoven.com/livesite/xsl/HTMLTemplates.xsl"/-->
<!--xsl:include href="http://www.interwoven.com/livesite/xsl/StringTemplates.xsl"/-->
<xsl:template match="/">
<!-- TODO put section -->
<script>
var quickCatalogueCategories={
<xsl:for-each select="//Datum[#Name='Catalog']//CatalogContainer">
<!-- you are already in the node, just select the value of the children -->
<xsl:text> '{</xsl:text><xsl:value-of select="CatalogKey"/><xsl:text>'}</xsl:text>:'http://www.news.com/{'<xsl:value-of select="CatalogView"/>}',}
</xsl:for-each>
}
</script>
</xsl:template>
</xsl:stylesheet>
Related
I am trying to do XSL transformation in Edge and using a document node as xslt parameter(via XSLTProcessor.setParameter).
The transformToFragment returns null in Edge (and Chrome), but works ok in Firefox.
Is there a way to make the code below working (at least in Edge) or is there no support of document-node as a xslt parameter?
Below is a minimal code to present the issue.
https://jsfiddle.net/BennyHilarious/nxzLf9g3/1/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<script type="text/javascript">
function example() {
let xsldocp = new DOMParser();
const xsldoc = xsldocp.parseFromString(`<?xml version='1.0'?>
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method='xml' omit-xml-declaration='yes'/>
<!-- Parameters -->
<xsl:param name='ntParam'/>
<xsl:param name='strSpaces' select='" "'/> <!-- Default to 4 spaces -->
<!-- Global variables -->
<xsl:variable name='numTransformLevel' select='$ntParam/PARAMETER/#VALUE'/> <!-- does not work in Edge & Chrome -->
<!--xsl:variable name='numTransformLevel' select='2'/--><!-- works OK -->
<!-- Handles a generic element node. -->
<xsl:template match='*'>
<div>
<b><xsl:value-of select='$numTransformLevel'/></b>
<i><xsl:value-of select='//GROOT/#VALUE'/></i>
</div>
</xsl:template>
</xsl:stylesheet>
`,"application/xml");
let xsltProcessor = new XSLTProcessor();
// use the xsl sheet
xsltProcessor.importStylesheet(xsldoc);
// create xml
let parser = new DOMParser();
const docData = parser.parseFromString("<ROOT><GROOT VALUE='IAM'/></ROOT>", "application/xml");
// create param xml and set the parameter
parser = new DOMParser();
const docParam = parser.parseFromString("<PARAM><PARAMETER VALUE='5'/></PARAM>", "application/xml");
xsltProcessor.setParameter(null, "ntParam", docParam.documentElement);
// do the transformation
var targetElm = document.getElementById("trCont5");
let resultFrag = xsltProcessor.transformToFragment(docData, targetElm.ownerDocument);
targetElm.appendChild( resultFrag );
}
</script>
</head>
<body>
<input type="button" onclick="example();" value="XLSTParam Test"/><!-- Outputs 5IAM -->
<div id="trCont5"></div>
</body>
</html>
Using SaxonJS (docs at https://www.saxonica.com/saxon-js/documentation2/index.html, download at https://www.saxonica.com/download/javascript.xml):
function example() {
const xslt = `<?xml version='1.0'?>
<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'/>
<!-- Parameters -->
<xsl:param name='ntParam'/>
<xsl:param name='strSpaces' select='" "'/> <!-- Default to 4 spaces -->
<!-- Global variables -->
<xsl:variable name='numTransformLevel' select='$ntParam/PARAMETER/#VALUE'/> <!-- does not work in Edge & Chrome -->
<!--xsl:variable name='numTransformLevel' select='2'/--><!-- works OK -->
<!-- Handles a generic element node. -->
<xsl:template match='*'>
<div>
<b><xsl:value-of select='$numTransformLevel'/></b>
<i><xsl:value-of select='//GROOT/#VALUE'/></i>
</div>
</xsl:template>
</xsl:stylesheet>
`;
// create xml
let parser = new DOMParser();
const docData = parser.parseFromString("<ROOT><GROOT VALUE='IAM'/></ROOT>", "application/xml");
// create param xml and set the parameter
const docParam = parser.parseFromString("<PARAM><PARAMETER VALUE='5'/></PARAM>", "application/xml");
// do the transformation
var targetElm = document.getElementById("trCont5");
let result = SaxonJS.XPath.evaluate(`
transform(map {
'source-node' : .,
'stylesheet-text' : $xslt,
'stylesheet-params' : map {
QName('', 'ntParam') : $ntParam
}
})?output`,
docData,
{
'params' : {
ntParam : docParam.documentElement,
xslt : xslt
}
});
targetElm.appendChild( result );
}
</script>
</head>
<body>
<input type="button" onclick="example();" value="XLSTParam Test"/><!-- Outputs 5IAM -->
<div id="trCont5"></div>
</body>
<script src="https://martin-honnen.github.io/xslt3fiddle/js/SaxonJS2.js"></script>
<input type="button" onclick="example();" value="XSLTParam Test"/><!-- Outputs 5IAM -->
<div id="trCont5"></div>
Note that for compactness and self-containedness of the example I used the SaxonJS.XPath.evaluatemethod to call the XPath 3.1 transform function on the fly to run the provided XSLT. SaxonJS also allows you to run precompiled XSLT with the SaxonJS.transform method, for that you need to use either SaxonEE or the Node.js xslt3 tool (from Saxonica/NPM https://www.npmjs.com/package/xslt3) to compile the XSLT file to a JSON file.
AJAX loaded xml and xsl then transform it to html. The result node is imported to html container. The result has script element with inline javascript code.
After Chrome update inline code won't executed.
For example:
HTML
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<script>
let buttonElement = document.createElement("button");
buttonElement.textContent = "Press me";
document.body.appendChild(buttonElement);
buttonElement.addEventListener("click", event => {
Promise.all([fetch('/test.xml'), fetch('/test.xsl')]).then(result => {
Promise.all([result[0].text(), result[1].text()]).then(result => {
let parser = new DOMParser(),
xml = parser.parseFromString(result[0], "application/xml"),
xsl = parser.parseFromString(result[1], "application/xml"),
target = document.body,
processor = new XSLTProcessor(),
output;
processor.importStylesheet(xsl);
output = processor.transformToDocument(xml);
let newOutput = document.body.appendChild(output.documentElement);
/*
Uncomment the block below, the result node its replacement helps and inline script will be executed properly
*/
/* newOutput.querySelectorAll("script").forEach(element => {
let replacement = document.createElement("script");
replacement.text = element.text;
element.replaceWith(replacement);
});
*/
});
});
}, false);
</script>
</body>
</html>
XML
<?xml version="1.0" encoding="UTF-8"?>
<hello>
<world/>
</hello>
XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" indent="no"/>
<xsl:template match="/">
<div>
<p>Hello world</p>
<script>console.log("Test me!");</script>
</div>
</xsl:template>
</xsl:stylesheet>
So, is it security bug of Webkit? Because, this bug was also detected in Opera.
And if this is security solution, why inline script is succesfully executed after result node replacement?
I have an input XML File:
<Items>
<Item Name="1" Value="Value1"></Item>
<Item Name="2" Value="Value2"></Item>
</Items>
What I want to transform to following output file with Saxon-CE.
<Items>
<Item Name="1" Value="NewValue1"></Item>
<Item Name="2" Value="NewValue2"></Item>
</Items>
JS:
function TransformXML() {
xsltData = Saxon.requestXML("transformXML.xsl");
xmlData = Saxon.requestXML("myxml.xml");
var xsltProcessor = Saxon.newXSLT20Processor(xsltData);
var result = xsltProcessor.transformToDocument(xmlData);
}
I have a mapping, that tells me the value of an item and the NewValue it should have after the transformation.
What I have so far:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*" />
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" name="xml" />
<xsl:variable name="Items">
<Item Name="1">
<Value OldValue="Value1" NewValue="NewValue1" ></Value>
</Item>
<Item Name="2">
<Value OldValue="Value2" NewValue="NewValue2" ></Value>
</Item>
</xsl:variable>
<xsl:template match="Items">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="Item">
<xsl:copy>
<xsl:if test="not($Items/Item[#Name=current()/#Name]/Value[#OldValue = current()/#Value])">
<!-- if value mismatch throw exception and stop -->
</xsl:if>
<xsl:attribute name="Value">
<xsl:value-of select="$Items/Item[#Name=current()/#Name]/Value[#OldValue = current()/#Value]/#NewValue"/>
</xsl:attribute>
<xsl:copy-of select="#*[name()!= 'Value']" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This is working, but what I want to do now is to check if there is a value mismatch.
If yes then the script should stop and throw an exception what I can catch in the Javascript function.
Is there a way to realize this? Can I set a callback function for this?
With XSLT, you can terminate processing using <xsl:message select="'some message'" terminate="yes"/>. It would be desirable that the transformToDocument method throws a Javascript exception you could catch with try/catch but it does not seem to happen, at least not in a quick test I did. I was however able to set an error handler and handle that message in that handler, see http://home.arcor.de/martin.honnen/xslt/test2014120301.html for an example, which does
function onSaxonLoad()
{
Saxon.setLogLevel('SEVERE');
var errors = [];
Saxon.setErrorHandler(function(error) { errors.push(error); });
var xslt = Saxon.requestXML("test2014120301.xsl")
var xsltProc = Saxon.newXSLT20Processor(xslt);
xsltProc.setInitialTemplate("main");
var errorCount = errors.length;
var doc = xsltProc.transformToDocument();
if (errorCount < errors.length) {
var msg = errors[errors.length - 1].message;
var pre = document.createElement('pre');
pre.textContent = msg;
document.body.appendChild(pre);
}
else {
// use result document doc here
}
}
The message Saxon pushes to the error handler is SEVERE: XPathException in invokeTransform: Processing terminated by xsl:message in test2014120301.xsl. Obviously there could be other errors pushed to the error handler so you will need more checks on the Javascript side to perhaps check that the message test contains Processing terminated by xsl:message.
I am trying to generate an XML file that list XML files that are in a specified folder using xsl:
XML file :
<xml>
<folder>FolderPath-to-List</folder>
</xml>
Expected result:
<mergeData newRoot="newRoot">
<fileList>
<fileItem>path-to-file/file1.xml</fileItem>
<fileItem>path-to-file/file2.xml</fileItem>
<fileItem>path-to-file/file3.xml</fileItem>
<fileItem>path-to-file/file4.xml</fileItem>
</fileList>
</mergeData>
So far I am able to collect Files list using XSL and embedded script/ JScipt function as follow in the current folder:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="http://tempuri.org/msxsl"
>
<msxsl:script language="JScript" implements-prefix="user">
<![CDATA[
var fso = new ActiveXObject("Scripting.FileSystemObject");
function ShowFolderFileList(folderspec)
{
var f, f1, fc, s;
f = fso.GetFolder(folderspec);
fc = new Enumerator(f.files);
s = '<fileItem>';
for (; !fc.atEnd(); fc.moveNext())
{
s += fc.item();
s += '<fileItem>\n<fileItem>';
}
return(s);
}
]]>
</msxsl:script>
<xsl:template match="/">
<mergeData newRoot="Activity">
<fileList>
<xsl:value-of select="user:ShowFolderFileList('.')"/>
</fileList>
</mergeData>
</xsl:template>
</xsl:stylesheet>
But the result is that in place of getting <fileItem> and </fileItem>, I have :
<fileItem>path-to-xml\file.xml<fileItem>
How can I get <fileItem>path-to-xml\file.xml</fileItem>?
How can I get the "FolderPath-to-List" from my XML to be used when calling user:ShowFolderFileList() in place of '.' so far to get it running.
First thing to note is that is that currently you are doing this
<xsl:value-of select="user:ShowFolderFileList('.')"/>
When really you ought to be doing this to use your file path in your XML
<xsl:value-of select="user:ShowFolderFileList(string(xml/folder/text()))" />
Note the use of "string()" here, because "text()" actually returns a text node, a not a datatype of string.
Secondly, when you use javascript functions in XSLT in this way, I believe they can only return the simple data types of string and number. Your function is returning a string, not actual XML, when you use xsl:value-of on a string, any reserved symbols will be escaped.
Now, you can be a bit naughty and do this
<xsl:value-of select="user:ShowFolderFileList(string(xml/folder/text()))"
disable-output-escaping="yes" />
But this is not necessarily considered good practise, as disable-output-escaping is not widely supported (although obviously it works in Mircosoft's implementation).
However, the only other way to do this (in XSLT 1.0) that I can think of is to return a list of file names, separated by new lines, and write a recursive template. Try this XSLT as an example:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="http://tempuri.org/msxsl"
exclude-result-prefixes="msxsl user">
<xsl:output method="xml" indent="yes"/>
<msxsl:script language="JScript" implements-prefix="user">
<![CDATA[
var fso = new ActiveXObject("Scripting.FileSystemObject");
function ShowFolderFileList(folderspec)
{
var f, f1, fc, s;
f = fso.GetFolder(folderspec);
fc = new Enumerator(f.files);
s="";
for (; !fc.atEnd(); fc.moveNext())
{
s += fc.item() + "\n";
}
return s;
}
]]>
</msxsl:script>
<xsl:template match="/">
<mergeData newRoot="Activity">
<fileList>
<xsl:call-template name="files">
<xsl:with-param name="files" select="user:ShowFolderFileList(string(xml/folder/text()))"/>
</xsl:call-template>
</fileList>
</mergeData>
</xsl:template>
<xsl:template name="files">
<xsl:param name="files"/>
<xsl:if test="normalize-space($files) != ''">
<file>
<xsl:value-of select="substring-before($files, '
')"/>
</file>
<xsl:if test="contains($files, '
')">
<xsl:call-template name="files">
<xsl:with-param name="files" select="substring-after($files, '
')"/>
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
There are a couple of other options though:
Don't use XSLT for this!
Upgrade to XSLT 2.0 (Mircosoft don't support it, but you can get other XSLT processors for .Net)
See this question which basically asks the same thing:
XSLT: How to get file names from a certain directory?
I saw a lot of posts online about MSXML4 to 6 or XSLT 1.0 versus 2.0 etc. But they could not answer my question.
I have a XSLT transformation code that works with MSXML4 APIs (XSLTransform and FreeThreadedDomDocument) on IE7 via Javascript.
Same code doesnt work with with MSXML6 APIs (XSLTransform and DomDocument) on IE9 via Javascript. It throws this error
"Namespace 'urn:mynamespace:mytable:transactions' does not contain any functions"
I have made sure that my ActiveX are enabled for MSXML4 and 6 both on IE9. Below is the code of the main tranformer XSLT, the reference XSLT & the JS code ...
Core XSLT: functions.xsl
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:myfuncs="urn:mynamespace:mytable:transactions" >
<msxsl:script language="javascript" implements-prefix="myfuncs">
<![CDATA[
// convert system GMT time into local time
// usage: <xsl:value-of select="myfuncs:localDateTime(datetime)"/>
var openBalance = 0;
function setOpenBalance(openBal)
{
openBalance = openBal;
}
function getOpenBalance()
{
openBalance = openBal;
return openBalance ;
}
]]>
</msxsl:script>
</xsl:stylesheet>
Main XSLT: MyTransformer.xsl ... that refers functions.xsl
<?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"
xmlns:myfuncs="urn:mynamespace:mytable:transactions">
<xsl:output method="xml"/>
<xsl:include href="functions.xsl" />
<!--<xsl:variable name="trade_cur_bal" select="myfuncs:getOpenBalance(100)"/>-->
<xsl:template match="/">
<Response>
<!-- Some working code here -->
</Response>
</xsl:template>
</xsl:stylesheet>
JS Code
var domXsl = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.4.0");
/*
// In case of IE9 ....
var domXsl = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.6.0");
*/
var domHTML = new ActiveXObject("Msxml2.XSLTemplate.4.0");
/*
// In case of IE9 ....
var domHTML = new ActiveXObject("Msxml2.XSLTemplate.6.0");
*/
domXsl.async=false;
domXsl.load("MyTransformer.xsl");
domHTML.stylesheet = domXsl;
var domData = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.4.0");
var input = "<MyInputData></MyInputData>"
domData.loadXML(input);
var result = tranform(domHTML, domData); //Works for MSXML 4.0 and fails for MSXML 6.0
function transform(template_, input_) {
var output = "";
if (input_ != null && input_.xml != "") {
var proc = template_.createProcessor();
proc.input = input_;
proc.transform();
output = proc.output;
delete proc;
}
return output;
}
Can someone guide me where am I going wrong w.r.t. MSXML6 or IE9?
Thx.
With MSXML 6 use of script inside XSLT is disabled by default for security reasons, so you need to explicitly enable it by calling
var domXsl = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.6.0");
domXsl.setProperty("AllowXsltScript", true);
And additionally to allow the use of xsl:import or xsl:include you also need to set
domXsl.setProperty("ResolveExternals", true);
I got this fixed by removing the versions (4 and 6) form the activeX class ID
e.g. new ActiveXObject("Msxml2.FreeThreadedDomDocument") etc.