![]() |
Web Development »
Client side scripting »
General
Intermediate
Browsing XML/XSL/XSD with HTA/Scripting RuntimeBy Dmitry KhudorozhkovThis article presents an XML/XSL/XSD browsing and validation tool, a shining example of how various technologies like Shell scripting, Scripting Runtime, or HTA can be put together to aid programmers in rapid development of powerful script-based applications. |
Javascript, XML, XSLT, Windows, Visual-Studio, IE5.5, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
The initial idea of this article was to bring you a simplistic example of using a Scripting Runtime Library, "click here and here, blah-blah-blah, thank you". The reason why I began writing was the need to make my and my colleagues' scripts file-system-aware. This ability proved to be very useful for software prototyping purposes or for building some small utilities; of course, it shouldn't be used on web (due to security/privacy reasons we'll discuss later).
Back to the tool. Prior to writing it, I was deep in XML for several months, so what you see here is an XML/XSLT viewer/browser, enveloped in the form of an HTML application. It helped me a lot when I was learning XML/XSL, now it aids <some other people> in rapid checking and bug tracking of large numbers of XSL templates; hope it helps you too.
Of course, this little browser (I'll call it "Xbrowser" further on) is in no way a replacement for any enterprise-grade development tool. It is just:
The last minute additions to this article were XML/XSL transformation and XML/XSD validation tools, which use (most of) the techniques described here.
For this piece of code to work properly, you'll need the following code packs:
If you look inside the attached archive, you'll see that the "Xbrowser" is no more than an HTML form. Let's see how to use it, and how the code works behind the stage, step by step.

Step 1: choose a folder where your XML files are located.
This part uses the Shell object, specifically its BrowseForFolder method.
function BrowseFolder()
{
// Accessing the Shell:
var objShell = new ActiveXObject("Shell.Application");
// Calling the browser dialog:
var objFolder = objShell.BrowseForFolder(0, "Select a folder:", 0);
if (objFolder == null) return "";
// Accessing the folder through FolderItem object:
var objFolderItem = objFolder.Items().Item();
var objPath = objFolderItem.Path;
var foldername = objPath;
if (foldername.substr(foldername.length-1, 1) != "\\")
foldername = foldername + "\\";
// foldername is the actual folder name.
...
}

Step 2: choose a file.
Two interesting things here:
Scripting.FileSystemObject is the main point of access to the file system. In short:
FileSystemObject contains: |
|
Drives collection contains: |
|
Folders collection contains: |
|
Folder object contains: |
|
Files collection contains: |
|
File object contains: |
|
The FSO has lots of collections, methods, and properties; I've just pointed out the most commonly used ones.
Enumerator object is a simple iterator, used to cycle through the collection of objects:
Enumerator object contains: |
|
var fc = new Enumerator(colFiles);
for (; !fc.atEnd(); fc.moveNext())
{
var objFile = fc.item();
...
}// Here goes the Scripting Runtime, FileSystemObject object:
var objFSO = new ActiveXObject("Scripting.FileSystemObject");
// Accessing the folder:
var objFolder = objFSO.GetFolder(curXMLfolder);
// Accessing the files:
var colFiles = objFolder.Files;
var xmlcount = 0, xslcount = 0;
// Cycling through the files, one by one:
var fc = new Enumerator(colFiles);
if (fc.atEnd() != true)
// If collection of files is not empty...
{
for (; !fc.atEnd(); fc.moveNext())
// Iterating through the files
{
var objFile = fc.item();
var ftext = objFile.Name.toLowerCase();
// Checking the extension:
if ((ftext.substr(ftext.length-3, 3)=="xml") ||
(ftext.substr(ftext.length-3, 3)=="rdf"))
{
xmlcount = xmlcount + 1;
// Opening the <SELECT> tag is any XML files exist:
if (xmlcount == 1)
xmlsel="<SELECT id='xmlselection' onchange='refresh()'>";
// Adding an option:
xmlsel=xmlsel+"<OPTION value="+ftext+">"+
ftext+"</OPTION>";
// Closing the tag:
if (fc.atEnd()) xmlsel=xmlsel+"</SELECT>";
}
}
}
This is the MSXML's part:
// Creating the new empty DOM tree:
var xml = new ActiveXObject("MSXML2.DOMDOCUMENT");
// No asynchronous load:
xml.async = false;
// Loading the file from disk:
xml.load(curXMLfolder + xmlselection.value);
Doing the same for the stylesheet:
// Creating the new empty DOM tree:
var xsl = new ActiveXObject("MSXML2.DOMDOCUMENT");
// No asynchronous load:
xsl.async = false;
// Loading the file from disk:
xsl.load(curXSLfolder + xslselection.value);
Loading XML data from a string is a bit different from loading a file. No files, no options; all you must do is to write a string which will contain your XML code. Then, you parse that string with a single call to the LoadXML method:
// Defining a string - default stylesheet:
var defsheet="<?xml version=\"1.0\"?>";
...
defsheet += "</xsl:stylesheet>";
// String -> DOM:
if(!defSheetCache)
{
defSheetCache = new ActiveXObject("MSXML2.DOMDocument");
defSheetCache.async = false;
defSheetCache.resolveExternals = false;
defSheetCache.loadXML(defsheet);
}
Here, LoadXML is used for loading a default stylesheet (hard-coded in a string), used when no XSL files are found in the appropriate folder.


Step 3: review the validation result.
The actual validation takes place immediately after the XML document has finished loading:
...
xml.load(curXMLfolder + xmlselection.value);
// Document is already validated;
// 'xml.parseError.errorCode' contains error code, if any.
...
So, all you must do is to check:
if (xml.parseError.errorCode != 0)
{
// Handle error
}
else
{
// Proceed - XML is ok.
}
To validate your XML document against a schema, the script does the following:
...
if(xslFile.substr(xslFile.length - 3, 3) == "xsd")
{
// 1. Loading XSD schema into the DOMDocument:
var schemaSource = new ActiveXObject("MSXML2.DOMDocument.4.0");
if(!schemaSource.load(curXSLfolder + xslFile))
{
xslErrorCache = schemaSource.parseError.errorCode +
": " + schemaSource.parseError.reason;
passedXSL.innerHTML = "... Schema is corrupt ...";
result.innerHTML = "";
return;
}
// 2. Extracting the targetNamespace
// attribute from the schema:
schemaSource.setProperty("SelectionLanguage", "XPath");
schemaSource.setProperty("SelectionNamespaces",
"xmlns:xs='http://www.w3.org/2001/XMLSchema'");
var tnsattr = schemaSource.selectSingleNode("/*[local-name()" +
"='schema']/@targetNamespace");
var nsuri = tnsattr ? tnsattr.nodeValue : "";
// 3. Creating/purifying the schema cache:
if(!schemaCache)
schemaCache = new ActiveXObject("Msxml2.XMLSchemaCache.4.0");
else
{
for(var i = 0; i < schemaCache.length; i++)
{
schemaCache.remove(schemaCache.namespaceURI(i));
}
}
// 4. Adding schema to the schema cache:
schemaCache.add(nsuri, schemaSource);
...
// 5. Binding schema cache to an empty DOMDocument:
var xmlSource = new ActiveXObject("MSXML2.DOMDocument.4.0");
xmlSource.schemas = schemaCache;
xmlSource.async = false;
// 6. Loading the document:
if(!xmlSource.load(curXMLfolder + xmlFile))
{
xslErrorCache = xmlSource.parseError.reason;
passedXSL.innerHTML =
"... XML document doesn't conform to schema ...";
result.innerHTML = "";
return;
}
else
{
result.innerHTML = xml.transformNode(xsl.documentElement);
passedXSL.innerHTML =
"... XML document conforms to schema ...";
}
}
Please take a note: this validation procedure requires you to have MSXML 4.0 installed.
Once the XML and the XSL files are loaded into DOM trees, transforming the XML data with a stylesheet is as easy as nothing:
resultCache = xml.transformNode(xsl.documentElement);
One problem arises with resultCache: if the input XSLT document generates embedded stylesheet (<STYLE>) blocks, these will be stripped from the resulting HTML after we display it through result.innerHTML. This problem can be solved by extracting the style definition from the result and incorporating it into the browser's document:
var elem = document.createStyleSheet();
elem.cssText =
trim(resultCache.substring(resultCache.indexOf("<style>") + 7,
resultCache.indexOf("</style>")));
elem.title = "user_styles";
To avoid style conflicts, we must "collect garbage" immediately before every transformation:
// ...somewhere at the beginning of 'refresh()':
// Clear styles generated by previous XSLT stylesheet, if any:
var stls = document.getElementsByTagName("style");
for(var k = 0; k < stls.length; k++)
{
var stl = document.styleSheets[k];
if (stl.title == "user_styles")
{
var r = stl.rules.length;
for(var j = 0; j < r; j++)
stl.removeRule[0];
break;
}
}

Step 4: save the XSLT output to a file.
Two points of interest here: the "Save" dialog, and the file creation process itself. For the "Save" dialog to work, you must register and obtain a design-time license for the following ActiveX component:
<object id="cmdlg"
classid="clsid:F9043C85-F6F2-101A-A3C9-08002B2F49FB"
codebase="http://activex.microsoft.com/controls/vb6/comdlg32.cab">
</object>
Then, you can use it:
function fileSave()
{
cmdlg.CancelError = false;
cmdlg.FilterIndex = 1;
cmdlg.DialogTitle = "Save file as";
cmdlg.Filter = "HTML file (*.html)|*.html|XML file (*.xml)|*.xml";
// Calling the dialog:
cmdlg.ShowSave();
return cmdlg.FileName;
}
Wishing to save XSLT output, we simply take resultCache and stream it down to a file. No need to check for any errors here, because we don't show the "Save..." button if either of the two documents (XML or XSL) hasn't passed validation.
function Save()
{
// Asking for file name:
var filename = fileSave();
if (filename != "")
{
// Creating the file:
var objFSO = new ActiveXObject("Scripting.FileSystemObject");
var objFile = objFSO.CreateTextFile(filename);
// Writing the XSLT output:
objFile.Write(resultCache);
objFile.Close();
}
}As a test bed for the presented utility, the accompanying Zip file contains the NASDAQ historical price data of Sun Microsystems Inc., along with three XSLT stylesheets I've written:

<xsl:choose> rule:

<xsl:sort> rule); and, of course, an algorithm for building stylish bar charts:

The last little things included are:
transfrm.js batch.list
The file, passed as an argument, may contain an arbitrary number of lines (one transformation per line) in the following format:
<xml_file_name>,<xsl_file_name>,<result_file_name>
Sample batch list:
stock.xml,plain.xsl,result1.html
stock.xml,table.xsl,result2.html
stock.xml,bargraph.xsl,result3.html
Aside from the resulting files, the script creates, or appends to, the "transfrm.log" file, which contains a transformation log.
transfrm.js input.xml input.xsl output_file.html
The "transfrm.log" file will be populated with the information on the last transformation.
validate.js input.xml input.xsd
As a result, you'll have a message box, saying if the input.xml conforms to input.xsd or not.
validate.js batch.file
The batch file contains the list of input XML files and XSD schemas:
<xml_file_1_name>,<xsd_file_1_name>
<xml_file_2_name>,<xsd_file_2_name>
The script creates, or appends to, the "validate.log" file with the details of the last validation.
Looking at the previous sections, one can guess: a script that writes arbitrary data to arbitrary files can be a big source of headache and security problems. Moreover, HTML applications are not subject to IE's security restrictions (see the appropriate introduction), so third-party (or just erroneous) scripts that use the FileSystemObject can be a major security threat.
This dictates two primary uses of the Scripting Runtime: "local" (non-web) utilities, and server-side scripting. As MSDN says, "because the use of the FSO on the client side raises serious security issues about providing potentially unwelcome access to a client's local file system, this documentation assumes the use of the FSO object model to create scripts executed by Internet Web pages on the server side. Since the server side is used, the Internet Explorer default security settings do not allow the client-side use of the FileSystemObject object. Overriding those defaults could subject a local computer to unwelcome access to the file system, which could result in total destruction of the file system's integrity, causing loss of data, or worse."
Sometimes, the only option you can look at is turning stand-alone HTAs to corporate web pages (simply renaming the .hta file to .html and ripping the HTA:APPLICATION tag), thus using the FSO at client-side. It raises problems with component licensing and execution permissions; furthermore, you must be sure that your intranet is extremely secure. In this case (i.e., if you're returning to "ordinary web"), in order to defend yourself from any unexpected behavior, and, on the other side, use the power of advanced scriptable objects (like Scripting.FileSystemObject or MSXML.DOMDocument), please consider the following:
Please note, that these are just basic rules; you should probably consult with IT professionals to build up your intranet security to the appropriate level.
Other downsides of the "Xbrowser" are: it is strictly IE-bound, and it depends too much on external code libraries. This is the flipside of the power that the IE engine provides; however, dependencies can be reduced in a number of ways.
XML for <SCRIPT> - cross Platform standards-compliant XML parser in JavaScript. Pros: W3C DOM (level 2)/SAX parsers included, together with an XPath processor. Cons: if you need a schema/DTD-aware parser, this is not your choice. (Almost) Perfect for cross-browser work.
Sarissa - not a parser, but a JavaScript wrapper for native XML APIs. DOM operations, XSL transformations, and XPath queries can be performed; all popular browsers are supported. This can (also) help you build cross-browser XML solutions.
Unfortunately (or fortunately?), there is no standard alternative to the Scripting Runtime Object Library for file I/O. Other browsers (Mozilla Firefox, Opera, etc.) don't tolerate any deviations from the ECMAScript standard (Microsoft's JScript is the implementation of it), so you can be sure that no code is capable of tearing your file system apart.
For developing some serious XML applications, you'll need something more than a Notepad.
Formal technical specifications, written, as usual, in W3C's heavy language:
The Web has more papers on XML/XSLT than any ordinary human can read for life. Here're just a few links:
| You must Sign In to use this message board. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+PgUp/PgDown to switch pages.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 7 Jun 2006 Editor: Smitha Vijayan |
Copyright 2005 by Dmitry Khudorozhkov Everything else Copyright © CodeProject, 1999-2010 Web17 | Advertise on the Code Project |