使用 HTA/Scripting Runtime 浏览 XML/XSL/XSD






4.61/5 (14投票s)
2005 年 11 月 14 日
12分钟阅读

111189

1390
本文介绍了一个 XML/XSL/XSD 浏览和验证工具,它生动地展示了如何将 Shell 脚本、Scripting Runtime 或 HTA 等各种技术结合起来,帮助程序员快速开发强大的脚本应用程序。
内容概要
本文的初衷是给您提供一个使用 Scripting Runtime 库的简单示例,“点击此处和此处,等等,谢谢”。我开始写作的原因是需要让我的同事和我自己的脚本能够感知文件系统。事实证明,这种能力对于软件原型设计或构建一些小型实用工具来说非常有用;当然,由于我们稍后将讨论的安全/隐私原因,它不应该用于 Web。
回到工具本身。在编写此工具之前,我花了几个月时间深入研究 XML,因此您在此看到的是一个 XML/XSLT 查看器/浏览器,它以HTML 应用程序的形式呈现。这在我学习 XML/XSL 时给了我很大帮助,现在它帮助<其他人>快速检查和跟踪大量的 XSL 模板;希望它也能帮助您。
当然,这个小浏览器(我以后称之为“Xbrowser”)绝不是任何企业级开发工具的替代品。它只是
- 一个交互式学习工具,为 XML 开发的初学者展示了使用 JScript/MSXML 处理 XML 的基础知识;也许还能为
- Office 工具和解决方案的开发者提供一个使用 Microsoft Scripting Runtime Object Library 的示例;当然,它也是
- 一个用于验证 XML 文档(是否格式良好/是否符合模式)和查看 XSLT 输出的简单实用工具。
本文的最后几项添加内容是XML/XSL 转换和XML/XSD 验证工具,它们使用了(本文描述的)大部分技术。
要求
为了使这段代码正常工作,您需要以下代码包
- 通用对话框 ActiveX 控件 - 提供了一套标准的对话框,用于打开和保存文件、设置打印选项以及选择颜色和字体。它随 MS Visual Basic 和 MS Office 2000/XP 产品一起提供,或者可以从 Microsoft 网站下载。
- Scripting Runtime 对象库 是 Microsoft 的文件系统管理解决方案,设计用于脚本语言;它是 Microsoft Office 2000/XP 的组成部分。此库也可在Microsoft 网站上下载。
- Microsoft XML Core Services 和/或 SDK(3.0 版或最好是 4.0 版)。可以从 Microsoft 网站下载。
- 安装上述部分程序包可能需要一个CAB 提取实用程序。您可以从 Microsoft 网站下载。
工作原理
如果您查看附带的存档,您会发现“Xbrowser”不过是一个 HTML 表单。让我们一步一步地看看如何使用它,以及后台代码是如何工作的。
- 文件夹浏览
第一步:选择 XML 文件所在的文件夹。
这部分使用了
Shell
对象,特别是它的BrowseForFolder
方法。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. ... }
- 文件浏览和枚举。
第二步:选择一个文件。
这里有两个有趣的地方
Scripting.FileSystemObject
是访问文件系统的主要入口。简而言之,FileSystemObject
包含Drives
集合Folders
集合Files
集合
GetDrive
方法(访问特定驱动器)。GetFolder
方法(访问特定文件夹)。GetFile
方法(访问特定文件)。
Drives
集合包含Item
属性(用于访问驱动器)。Count
属性(系统中驱动器的数量)。
Folders
集合包含Item
属性(用于访问文件夹)。Count
属性(集合中文件夹的数量)。
Add
方法(创建新文件夹)。
Folder
对象包含SubFolders
集合(文件夹的子文件夹,包括那些设置了隐藏和系统文件属性的)。Files
集合(访问文件夹中的所有文件)。
Files
集合包含Item
属性(用于访问文件)。Count
属性(文件夹中文件的数量)。
File
对象包含Name
属性(文件名)。Size
属性(文件大小)。DateCreated
属性(文件创建日期和时间)。
FSO 拥有大量的集合、方法和属性;我只列出了最常用的。
Enumerator
对象是一个简单的迭代器,用于遍历对象集合Enumerator
对象包含item
方法(返回集合中当前对象的引用)。atEnd
方法(当迭代器到达集合末尾时返回true
)。moveFirst
方法(迭代到集合中的第一个对象)。moveNext
方法(迭代到集合中的下一个对象)。
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>"; } } }
- 从文件加载 XML。
这是 MSXML 的部分
// 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);
对样式表执行相同操作。
// 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);
- 从字符串加载 XML。
从字符串加载 XML 数据与从文件加载略有不同。没有文件,没有选项;您需要做的就是编写一个包含 XML 代码的字符串。然后,您只需一次调用
LoadXML
方法即可解析该字符串。// 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); }
此处,
LoadXML
用于加载默认样式表(硬编码在字符串中),当在相应文件夹中找不到 XSL 文件时使用。 - 文档验证。
第三步:查看验证结果。
实际验证在 XML 文档加载完成后立即进行。
... xml.load(curXMLfolder + xmlselection.value); // Document is already validated; // 'xml.parseError.errorCode' contains error code, if any. ...
所以,您只需要检查
if (xml.parseError.errorCode != 0) { // Handle error } else { // Proceed - XML is ok. }
- 使用任意 XSD 模式验证 XML 文档。
要使用模式验证 XML 文档,脚本会执行以下操作:
... 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 ..."; } }
请注意:此验证过程要求您安装 MSXML 4.0。
- 使用 XSL 样式表转换 XML。
一旦 XML 和 XSL 文件被加载到 DOM 树中,使用样式表转换 XML 数据就非常简单了。
resultCache = xml.transformNode(xsl.documentElement);
- 重新连接 CSS。
resultCache
会出现一个问题:如果输入的 XSLT 文档生成了嵌入的样式表(<STYLE>
)块,这些块在我们通过result.innerHTML
显示它们时会被剥离。通过将样式定义从结果中提取并合并到浏览器的文档中,可以解决此问题。var elem = document.createStyleSheet(); elem.cssText = trim(resultCache.substring(resultCache.indexOf("<style>") + 7, resultCache.indexOf("</style>"))); elem.title = "user_styles";
为避免样式冲突,我们必须在每次转换之前立即“垃圾回收”。
// ...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; } }
- 将转换结果保存到文件。
第四步:将 XSLT 输出保存到文件。
这里有两个值得关注的点:“保存”对话框和文件创建过程本身。为了使“保存”对话框正常工作,您必须注册并获取以下 ActiveX 组件的设计时许可证。
<object id="cmdlg" classid="clsid:F9043C85-F6F2-101A-A3C9-08002B2F49FB" codebase="http://activex.microsoft.com/controls/vb6/comdlg32.cab"> </object>
然后,您就可以使用它了。
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; }
如果希望保存 XSLT 输出,我们只需获取
resultCache
并将其流式传输到文件。这里无需检查任何错误,因为如果两个文档(XML 或 XSL)中的任何一个未通过验证,我们就不会显示“保存...”按钮。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(); } }
全部包含
作为对所介绍工具的测试平台,随附的 Zip 文件包含 Sun Microsystems Inc. 的NASDAQ 历史股价数据,以及我编写的三种 XSLT 样式表。
- 纯文本。这个文件显示原始 XML 数据,并带有 IE 的配色方案。
- 表格。这是一个简单的 XSL 转换示例。绿色/红色行显示股票价格的上涨/下跌,是
<xsl:choose>
规则的说明。 - 条形图。一个更复杂的样式表。这个文件包含:“for-next”风格的循环(通过命名模板的递归调用实现);搜索一行值中的最大值(使用
<xsl:sort>
规则);当然,还有一个用于构建时尚条形图的算法。
快速完成
最后包含的一些小东西是:
- transfrm 脚本 - 一个简单的 WSH 工具,用于将 XSLT 样式表应用于 XML 文档。该脚本有两种模式。
- 批量模式非常适合一次性执行大量 XSL 转换。该脚本将单个文件名作为参数。
transfrm.js batch.list
作为参数传递的文件可以包含任意数量的行(每行一个转换),格式如下:
<xml_file_name>,<xsl_file_name>,<result_file_name>
示例批处理列表
stock.xml,plain.xsl,result1.html stock.xml,table.xsl,result2.html stock.xml,bargraph.xsl,result3.html
除了结果文件外,该脚本还会创建或追加“transfrm.log”文件,其中包含转换日志。
- 在单次转换模式下,该脚本接受三个参数。
transfrm.js input.xml input.xsl output_file.html
“transfrm.log”文件将填充有关最后一次转换的信息。
- 批量模式非常适合一次性执行大量 XSL 转换。该脚本将单个文件名作为参数。
- validate.js 脚本 - 一个 WSH 工具,用于将单个 XML 文档针对任意 XSD 模式进行验证。
- 单次验证模式
validate.js input.xml input.xsd
结果是,您会看到一个消息框,说明 input.xml 是否符合 input.xsd。
- 批量模式
validate.js batch.file
批处理文件包含输入 XML 文件和 XSD 模式的列表。
<xml_file_1_name>,<xsd_file_1_name> <xml_file_2_name>,<xsd_file_2_name>
该脚本会创建或追加“validate.log”文件,其中包含最后一次验证的详细信息。
- 单次验证模式
安全
从前面的部分可以看出:一个可以将任意数据写入任意文件的脚本可能会带来很多麻烦和安全问题。此外,HTML 应用程序不受 IE 的安全限制(请参阅介绍),因此使用 FileSystemObject
的第三方(或有错误)脚本可能构成重大的安全威胁。
这决定了 Scripting Runtime 的两个主要用途:“本地”(非 Web)实用程序和服务器端脚本。正如MSDN 所说,“由于在客户端使用 FSO 会引发关于提供对客户端本地文件系统的潜在不受欢迎访问的严重安全问题,因此本文档假定使用 FSO 对象模型来创建由服务器端的 Internet Web 页面执行的脚本。由于使用的是服务器端,因此 Internet Explorer 的默认安全设置不允许在客户端使用 FileSystemObject
对象。覆盖这些默认设置可能会使本地计算机面临对文件系统的不受欢迎访问,从而导致文件系统的完整性被完全破坏,造成数据丢失,甚至更糟。”
有时,您唯一能考虑的选项是将独立的 HTA 转换为企业网页(只需将 .hta 文件重命名为 .html 并删除 HTA:APPLICATION
标签),从而在客户端使用 FSO。这会带来组件许可和执行权限的问题;此外,您必须确保您的内网是极其安全的。在这种情况下(即,如果您返回“普通 Web”),为了保护自己免受任何意外行为的影响,同时利用高级脚本对象(如 Scripting.FileSystemObject
或 MSXML.DOMDocument
)的强大功能,请考虑以下事项:
- 切勿允许未经您明确批准的非安全和未签名 ActiveX 组件运行。在 IE 的安全选项卡中,将“初始化和脚本化未标记为安全的 ActiveX 控件”选项设置为“提示”。
- 切勿允许未经您明确批准下载和运行 Java 组件。在 IE 的安全选项卡中,将 Java 虚拟机安全级别设置为“中”或“高”。
- 通常,从家庭或企业内网下载的脚本是可信的;因此,您可能希望将“本地 Intranet”的安全级别设置为“中”甚至“低”,同时将“Internet”安全级别设置为“高”。
- 将您的脚本所在的服务器添加到“受信任的站点”区域。
请注意,这些只是基本规则;您可能需要咨询 IT 专业人员,以将您的内网安全措施构建到适当的级别。
替代方案
“Xbrowser”的其他缺点是:它严格绑定于 IE,并且过于依赖外部代码库。这是 IE 引擎提供的强大功能的另一面;然而,可以通过多种方式减少依赖性。
- XML/XSL/XPath 处理
XML for <SCRIPT> - 跨平台符合标准的 JavaScript XML 解析器。优点:包含 W3C DOM(级别 2)/SAX 解析器以及 XPath 处理器。缺点:如果您需要支持模式/DTD 的解析器,这不是您的选择。(几乎)非常适合跨浏览器工作。
Sarissa - 不是解析器,而是原生 XML API 的 JavaScript 包装器。可以执行 DOM 操作、XSL 转换和 XPath 查询;支持所有流行浏览器。这(也)可以帮助您构建跨浏览器的 XML 解决方案。
- I/O
不幸(或幸运?)的是,对于文件 I/O,没有 Scripting Runtime Object Library 的标准替代方案。其他浏览器(Mozilla Firefox、Opera 等)不容忍任何偏离 ECMAScript 标准(Microsoft 的 JScript 是其实现)的代码,因此您可以确信没有任何代码能够摧毁您的文件系统。
链接
通用对话框控件
- MSDN 上的“在您的应用程序中集成 Windows 对话框功能”。
脚本
- MSDN 上的“可脚本化的 Shell 对象”。
- MSDN 上的“Microsoft Scripting Runtime 对象库”。
- MSDN 上的“HTML 应用程序简介”。
脚本安全
- MSDN 上的“安全性注意事项:动态 HTML”。
- MSDN 上的“浏览器用户的隐私”。
- MSDN 上的“设计安全的 ActiveX 控件”。
- MSDN 上的“ActiveX 控件的安全初始化和脚本化”。
- MSDN 上的“使用 Internet 组件下载在 Web 上部署 ActiveX 控件”。
- MSDN 上的“对 ActiveX 控件进行签名和标记”。
- MSDN 上的“每位开发人员都必须知道的十大安全提示”。
- MSDN 上的“脚本片段和 Web 浏览器安全如何交互”。
- MSDN 上的“如何启用 Internet 选项中的‘我的电脑’安全区域”。
- MSDN 上的“如何防止跨站点脚本安全问题”。
- Perl.org 上的“防止跨站点脚本攻击”。
XML
工具
为了开发一些严肃的 XML 应用程序,您需要比记事本更强大的工具。
- Altsoft XSL-FO Debugger - XSLT 样式表的视觉调试器;免费软件。
- RustemSoft XmlFox - 高级 XML/XSD/XSL 创作工具;免费软件。
- Morphon - XML/CSS 开发套件。免费软件,跨平台(Java)。该项目于 2003 年关闭,但仍然值得一看;请查看插件页面。
- XRay - XML 编辑环境;免费软件。
- XMLEditPro - XML 创作工具;免费软件。
- XNGR XML Browser - 浏览/编辑框架。免费软件,开源。
- MlView - Linux/Gnome 环境下的 XML 编辑器;免费软件。
- XMLMind XML Editor - 一个验证性 XML 编辑器;XML Editor 标准版免费。
- Altova XmlSpy - 一个集成开发环境(IDE),它包含了开发复杂 XML 应用程序所需的一切。XmlSpy 家庭版免费。
官方声明
正式的技术规范,一如既往地采用 W3C 的晦涩语言编写。
- XML - XML 和基于 XML 的技术的首页。
- XML Schema - 关于 XML 模式的一般信息;指向 XSD/DTD 辅助工具的链接。
- XSL - 可扩展样式表语言的首页。规范、软件新闻和链接、教程。
文章与代码示例
网上关于 XML/XSLT 的文章比任何普通人一生都能读完的还要多。这里只是一些链接。
- TopXML - #1 XML/XSL 网站。特别感兴趣的栏目:XSLT 样式表、代码库、文章库。
- XMLPitstop.com - 最大的 XML 文档和示例来源。
- XML.com - 您想了解的关于 XML 的一切(但不敢问)。
- XML Files - 很棒的网站,包含大量教程。
- Zvon.org - 教程、参考、工具和示例库。
- W3Schools.com 的 XML 和XSLT 在线教程。
- XML 设计模式 - 对开发人员来说非常有趣的信息。
- XML Everywhere - XML 应用程序、新闻和链接。
- SitePoint 的 XML/XSLT/Web 服务栏目 - 精彩的文章。