I am currently trying to implement a function to sort a XML file by the attribute of one of its nodes.
The way to go seemed to be using XSLT to transform the XML file and by researching a bit I managed to put together a working XSLT file (validated in some online-validator, output was as would be expected).
After I got the results I wanted, I started implementing JavaScript functions to do this automatically every time it is required. Ultimately a chart (amCharts) will be created using the data from the XML file. To generate that chart properly, the data has to be sorted though.
Right now everything seems to work syntax-wise, however, the XSLTProcessor and/or XMLSerializer seem to have problems parsing/combining both the XML and XLST file.
Strangely enough Firefox also throws an error: Component returned failure code: 0x80600001 [nsIXSLTProcessor.importStylesheet]
Chrome doesn't seem to have this problem.
But this is not the main issue. The result generated contains a rudimental HTML page saying that there was an error, but giving no exact error description/location/explanation whatsoever.
I expected to see the sorted XML file here, just as in the online XML/XSLT validator, which had the same inputs XML/XSLT content wise.
Does anyone have an idea regarding the source of this issue? Would greatly appreciate any hints and/or solutions to this.
best regards,
daZza
Edit
After researching the "Extra content at the end of the document" error on the generated page more in-depth (and having overlooked it when I posted the OP), it seems that the error is triggered when there are more than one root elements in the XML file, which would obviously be wrong.
However, the source XML files do not have multiple root nodes and are all perfectly valid.
This leads me to believe that the XSLT reordering/sorting of the source file is not done as expected, although it looked good in the XSLT validator.
I guess somehow it produces multiple root nodes instead of just reordering the item nodes within the root node?
Unfortunately I am no expert regarding XSLT so it would be awesome, if someone with a more detailed knowledge could especially look at the XSLT code.
Edit2
I think I might have solved the problem by changing the XSLT query slightly. I am getting a proper looking output for Chrome and Firefox, but IE still throws an error (did I mention that I hate cross-browser compatibilities? -.-).
Further testing is still required, but at least it's a progress. Still appreciating any hints regarding the topic though.
Code:
XML Sample Snippet (removed content, the "" parts are filled in the real file):
<?xml version="1.0" encoding="utf-8"?>
<root>
<item Art="" LinkTitle="" Eindruck="" Bereich="" Unterbereich="" Priority="" Handlungsma_x00df_nahme="" Status_x0020_der_x0020_Ma_x00df_="" Aufwand="" Benefit="" Termin_x0020_der_x0020_Retrospek="" Produkt="" Release="" />
<item Art="" LinkTitle="" Eindruck="" Bereich="" Unterbereich="" Priority="" Handlungsma_x00df_nahme="" Status_x0020_der_x0020_Ma_x00df_="" Aufwand="" Benefit="" Termin_x0020_der_x0020_Retrospek="" Produkt="" Release="" />
</root>
XSLT file
<?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:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:choose>
<xsl:when test="*[local-name()='item']">
<xsl:apply-templates select="#* | node()">
<xsl:sort select="#Unterbereich" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="#* | node()" />
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
JavaScript function
function sortXML()
{
var parser = new DOMParser();
// xml and xsl are correctly filled with the data from their appropriate files
// via AJAX GET requests earlier in the code
var domToBeTransformed = xml;
var xslt = xsl;
var processor = new XSLTProcessor();
processor.importStylesheet(xslt);
var newDocument = processor.transformToDocument(domToBeTransformed);
var serializer = new XMLSerializer();
var newDocumentXml = serializer.serializeToString(newDocument);
alert(newDocumentXml);
}
Currently generated document (var newDocumentXml)
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<parsererror style="display: block; white-space: pre; border: 2px solid #c77; padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; color: black">
<h3>This page contains the following errors:</h3>
<div style="font-family:monospace;font-size:12px">error on line 1 at column 1: Extra content at the end of the document</div>
<h3>Below is a rendering of the page up to the first error.</h3>
</parsererror>
</body>
</html>
Firstly, I would re-jig your XSLT to avoid the use of xsl:choose. There is no point in testing if a child element exists if you are already matching an attribute or text node. It may be better (and cleaner, with less indentation) to use separate templates.
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[*[local-name()='item']]">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:apply-templates select="node()">
<xsl:sort select="#Unterbereich" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The first thing to note is that you specified a version of "2.0" in your XSLT. Microsoft (and so IE) does not natively support XSLT 2.0. However, in this case, you are not using any XSLT 2.0 features, so changing it to XSLT 1.0 should not be a problem (Of course, if your actual XSLT does need to use 2.0, then that is a whole separate question!)
But I don't think this is an issue with your XSLT any more, but more an issue with how transformation is done in the browser with javascript. The code you have given will only work in Chrome and Firefox (and Opera, I think). IE doesn't support those commands, and has its own methods. And to compound matters further, IE10 and above do things differently to previous versions!
Anyway, I headed over to Object doesn't support property or method 'transformNode' in Internet Explorer 10 (Windows 8) and found a good sample of JavaScript. Try re-jigging your JavaScript to this:
function sortXML()
{
if (typeof (XSLTProcessor) != "undefined")
{
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xsl);
var resultDocument = xsltProcessor.transformToDocument(xml);
var serializer = new XMLSerializer();
var newDocumentXml = serializer.serializeToString(resultDocument);
alert(newDocumentXml);
}
else if (typeof (xml.transformNode) != "undefined")
{
var ex = xml.transformNode(xsl);
alert(ex);
}
else
{
var xslDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");
xslDoc.load(xsl);
var xslt = new ActiveXObject("Msxml2.XSLTemplate");
xslt.stylesheet = xslDoc;
var xslProc = xslt.createProcessor();
xslProc.input = xml;
xslProc.transform();
alert(xslProc.output);
}
}
Related
For some browsers, for some clients, jquery suddenly stopped parsing xml.
Example xml:
<?xml version="1.0" encoding="UTF-8"?>
<wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs" xmlns="http://www.opengis.net/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:example_namespace="https://mylink.com/wfs/example_namespace" xsi:schemaLocation="https://mylink.com/wfs/example_namespace mylink.com?SERVICE=WFS&VERSION=1.0.0&TYPENAME=example_namespace:TABLE&REQUEST=DescribeFeatureType" numberMatched="unknown" numberReturned="1">
<gml:featureMember>
<example_namespace:TABLE fid="TABLE.15">
<example_namespace:ID>15</example_namespace:ID>
</example_namespace:TABLE>
</gml:featureMember>
</wfs:FeatureCollection>
Example jquery
$.get(link, function(data) { //Response is xml like in example.
console.log(data) //works as before
console.log($(data).find("featureMember").find("ID").text())
//Doesn't work now for some clients, but worked before.
})
EDIT:
More general example:
XML
<?xml version="1.0" encoding="UTF-8"?>
<a xmlns:c="http://www.example.com">
<c:b>TEST</c:b>
</a>
AND JS:
console.log($(data).find("b").text())
now returns: "", before returns: "TEST"
EDIT 2:
Similar problem:
Cannot extract SOAP XML with jQuery
This code was originally working in Chrome last week and once Chrome
updated to v60 it no longer does.
Pass data to $.parseXML() to get a #document, use .querySelector()
$.parseXML(data).querySelector("featureMember").querySelector("ID").textContent;
alternatively pass result of $.parseXML() to jQuery() and use .find()
$($.parseXML(data)).find("featureMember").find("ID").text();
jsfiddle https://jsfiddle.net/5fuLquth/
Parsing XML using jQuery in this way has some browser compatibility bugs I believe. This has been a won't fix jquery bug previously.
When there are namespaces in the xml you can use jquery selectors like the below by escaping the colon:
.find('gml\\:featureMember')
or using nodeName:
.find('[nodeName="gml:featureMember"]')
See demo below:
var xml=`<?xml version="1.0" encoding="UTF-8"?><wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs" xmlns="http://www.opengis.net/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:example_namespace="https://mylink.com/wfs/example_namespace" xsi:schemaLocation="https://mylink.com/wfs/example_namespace mylink.com?SERVICE=WFS&VERSION=1.0.0&TYPENAME=example_namespace:TABLE&REQUEST=DescribeFeatureType" numberMatched="unknown" numberReturned="1"><gml:featureMember><example_namespace:TABLE fid="TABLE.15"><example_namespace:ID>15</example_namespace:ID></example_namespace:TABLE></gml:featureMember></wfs:FeatureCollection>`;
console.log($(xml)
.find('gml\\:featureMember')
.find("example_namespace\\:ID").text());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I'm getting a JSON object back from the API and converting that into XML which is all working okay, however when I try to use that converted object and wrapped it in XSL its not rendering any values in the HTML.
Can I do it something like this?
var x2js = new X2JS();
var json2XMLResult = x2js.json2xml_str(data.Results);
this.da = '<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="../../../config/Customer.xsl" type="text/xsl" ?> <Results>' + json2XMLResult + '</Results>';
and rendering it using document.getElementById().innerHTML
(<HTMLInputElement>document.getElementById('xmlJson')).innerHTML = this.da;
my XSL file:
<?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"
doctype-system="about:legacy-compat"
encoding="UTF-8"
indent="yes" />
<xsl:template match="/">
Account No. is - <xsl:value-of select="./Results/AccountNumber"/>
Last Name is - <xsl:value-of select="./Results/LastName"/>
</xsl:template>
</xsl:stylesheet>
Can someone point me to the right direction, please. I've never worked on XML nor XSL before so I'm completely lost.
Checking the HTML source it's commenting out the <?xml> tags.
I'm using XSLT to transform an XML into HTML on the client side (Chrome browser).
I'm trying to add <script> HTML tag to the XSLT but it seems that the code in it is never evaluated on the generated HTML, although I've specified defer.
On the other hand, onclick event itself runs OK.
Here is an example of the XSLT which demonstrates the issue:
<?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"
encoding="UTF-8"
indent="no"/>
<xsl:template match="/">
<html>
<head>
<script type="text/javascript" defer="defer">
<xsl:text>
<![CDATA[
function test(){
window.alert('Test');
}
]]>
</xsl:text>
</script>
</head>
<body>
<button onclick="window.alert('Test')">This works</button>
<br/>
<button onclick="test()">This does not work</button>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
The XML file does not matter is this example. You can try the above example on W3Schools online XSLT transformation
In this example, clicking on This does not work yields an error: Uncaught ReferenceError: test is not defined.
What am I doing wrong here?
Update
The problem only happens when I'm performing the XSLT transformation itself in javascript. Here is the piece of code that is doing that in my case:
var processor = new XSLTProcessor(),
htmlResult;
processor.importStylesheet(xsl);
htmlResult = processor.transformToFragment(xhr.responseXML, document);
document.getElementById("result").appendChild(htmlResult);
Update
I also need the following to work correctly when they appear in the XSLT file:
Loading external javascript files using script element:
<script type="text/javascript" src="/somelibrary.js" />
Bare <script> elements with javascript code in them, that call functions which are declared in an external javascript file, loaded by an earlier script element.
As far as I have researched, this is a known bug in Chrome reported in 2013 in https://code.google.com/p/chromium/issues/detail?id=266305. It does not look as any effort has been taken to fix it. However, the bug reporter also found a workaround, instead of using appendChild or insertBefore or similar to insert the fragment returned by transformToFragment directly into the document, if you call importNode first on the fragment, the script elements are then executed when you insert the imported fragment into the document.
I have written a test case http://home.arcor.de/martin.honnen/xslt/test2015111606.html which works fine for me a current version of Chrome on Windows, it uses
var proc = new XSLTProcessor();
proc.importStylesheet(xsltDoc);
targetElement.appendChild(targetElement.ownerDocument.importNode(proc.transformToFragment(xmlDoc, targetElement.ownerDocument), true));
However, do note that Microsoft Edge neither with the normal approach of calling appendChild on the fragment nor with the above Chrome workaround executes the script element's code.
Possible solution: (inspired from this answer)
function insertAndExecute(id, htmlResult) {
document.getElementById(id).appendChild(htmlResult);
var scripts = document.getElementById(id).getElementsByTagName("script");
var deferreds = [];
// First load all external scripts
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src != "") {
deferreds.push($.getScript(scripts[i].src));
}
}
// Execute other (inline) scripts after all external scripts are loaded
$.when.apply(null, deferreds).done(function() {
for (var i = 0; i < scripts.length; i++) {
if (!scripts[i].src) {
$.globalEval(scripts[i].innerText || scripts[i].textContent);
}
}
})
}
var processor = new XSLTProcessor(),
htmlResult;
processor.importStylesheet(xsl);
htmlResult = processor.transformToFragment(xhr.responseXML, document);
// The htmlResult may contain javascript code. Scan and run it
insertAndExecute("result", htmlResult);
I'm trying to develop a simple script that reads the words of a string, and compare with a dictionary .txt, .dic... or an Array Collection of correct words.
The format of my string is like this:
<data>
<value>Exmple</value>
</data>
<data>
<value>Boxx</value>
</data>
<data>
<value>Softvare</value>
</data>
<data>
<value>Adres</value>
</data>
He can only apply the fix on the words that you have within the <value> tag.
What he would have me return should be the same structure with the words corrected. This Form.
<data>
<value>Example</value>
</data>
<data>
<value>Box</value>
</data>
<data>
<value>Software</value>
</data>
<data>
<value>Adress</value>
</data>
I'm trying to get at first this result using some combinations .replace()
My application will run on a system without internet access. So I need to do something that does not require access to any web service, which requires consultation or at some other file linguaguem. So precisso be developing this checker in jquery / javascript PURE!
Is it possible to do this? If yes how can I be doing in my code? Example?
DEMO CODE
$('button').click(function () {
var code = $('textarea[name=message]').val();
if ($('#output').length < 1) {
$("body").append('<h2>Output</h2><textarea id="output" rows="10" cols="100"></textarea>');
}
$('#output').val(code);
});
My English is bad I hope my question has been clear and can be understood by you.
I have a C# application that generates an html document from transforming an xml file with an xsl file. In my xsl template I reference an external javascript file like this:
<script language="javascript" type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js" ></script>
after the transformation the previous line is being translated to:
<script language="javascript" type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js" />
For Firefox and Chrome this is no problem however IE throws an 'object not found' error and does not work. Any suggestions for getting IE to like this syntax? Or is there something I need to do in my xsl (or the C# XslCompiledTransform class) to preserve the syntax?
Solution: By placing <![CDATA[ <!-- Some Comment --> ]]> between the script tags the parser doesn't attempt to shorten the ending tag.
Try putting an empty CDATA section inside. This should force the parser to not mess with your script tags.
<script language="javascript" type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js" ><![CDATA[ ]]></script>
Actually, bobince is right. If you use...
<xsl:output method="html"/>
... you can get the right output for XslCompiledTransform, but you have to use its OutputSettings with the XmlWriter you use as output object:
XslCompiledTransform xslt = new XslCompiledTransform(true);
xslt.Load("stylesheetFile.xsl");
XmlWriter outputWriter = XmlWriter.Create("outputfile.html", xslt.OutputSettings);
xslt.Transform(input, null, outputWriter);
This way, the method="html" works, so script, textarea et al keep their closing tags.
Generate an XML comment inside <script>:
<script type="text/javascript" src="..." ><xsl:comment/></script>
The output will be:
<script type="text/javascript" src="..."><!-- --></script>
which is semantically equivalent to an empty <script>.
Not preserve it, but if you're producing backwards-compatible HTML you need to tell the XSLT processor that HTML-compatible-XHTML is what you want and not generic self-closing-allowed XML:
<xsl:output method="xhtml"/>
Unfortunately, the ‘xhtml’ output method is an XSLT 2.0 extension that .NET's XslTransform doesn't support, so you have to use good old legacy HTML instead:
<xsl:output method="html"/>
(and appropriate HTML 4.01 DOCTYPE instead of XHTML.)
Putting some dummy content in <script> may solve your immediate problem, but there may be other places where the default ‘xml’ output method will produce inappropriate markup for legacy browsers like IE.
Re: comment. Hmm... you're right! The ‘html’ output method does not produce valid HTML; the ‘xhtml’ output method does not produce XHTML conformant to XHTML Appendix C. What's more, ‘html’ includes provisions such as not escaping ‘<’, and de-escaping the ancient and broken-even-for-its-time Netscape 4 construct ‘&{...}’, that will take your working markup and make it invalid.
So changing the output method is completely useless, and the only way to produce working HTML with XSLT is:
a. hack every occurrence of an inappropriate self-closing tag manually (there are likely to be many more than just this script), or
b. post-process with something like HTMLTidy.
How sad, and sloppy that this hasn't been addressed even in XSLT 2.0.
had the same prob. right now, this is my solution:
<xsl:text disable-output-escaping="yes">
<![CDATA[<script type="text/javascript" src="" ></script>]]>
</xsl:text>
Just Missing the closing </script>.
<xsl:output method="html" omit-xml-declaration="yes" doctype-system="about:legacy-compat" encoding="utf-8"/>
should solve your probleme