您好,脚本专家!追汽车......和 XML

Microsoft 脚本专家

下载这篇文章的代码: HeyScriptingGuy2007_02.exe (150KB)

“我不知道 为什么我家的狗老是追汽车”,这是一个广为流传的老笑话,“就算有一天它真的追上一辆,又能怎么样呢?”

问得好,这是一个脚本专家都无法回答的问题。不过,Lucy(沿着大街追汽车的那条狗)除外。毫无疑问,这条狗不可能会抢劫一家酒水店,然后驾车逃之夭夭。不过,基于目前的情况,我敢打赌,她不会再造访沿途的任何其他店铺,您知道我们是指什么。

如果要在当今的高科技时代对这句陈词滥调进行更新的话,它可能会类似于此:“我不知道为什么系统管理员老是想精通 XML。就算她真的精通了,又能怎么样呢?”

请注意,在我们的辩词中只是说要更新那条令人厌倦的老笑话,而从未说过能让它好笑。

老实说,XML 可能是一项有些宣传过度的技术,至少对于系统管理员来说是如此。事实上,许多系统管理员从未编写过一个可与 XML 文件相交互的脚本,但却过着长寿而幸福的生活,而我们并不期望在不久的将来改变这一情况。

据说越来越多的应用程序采用了 XML 作为数据存储的标准,实情也的确如此。为什么不呢?因为没有什么比美化文本文件更为重要了,这些 XML 数据文件创建起来迅速而容易,可以跨平台使用,并且无需任何复杂(更谈不上昂贵)的数据库程序。您是否拥有操作系统和文本编辑器?如果有,那么您便可创建 XML 数据库。

当然,这意味着系统管理员至少可以用 XML 做一件事情:编写查询 XML 文件的脚本,就好像该文件是个功能齐全的数据库一样。到此,问一下,有没有谁可以就此情况提出比脚本专家更好的几点建议?

好吧,说的对。不过,如果必须大胆猜测的话,我敢打赌 Lucy 可能正在忙于钻研别人的精华,以便撰写一篇关于 XML 的专栏。

开始之前,让我们来看一个简单的 XML 文件(请参阅图 1)。这是一个数据库文件,由四部分脚本组成,我们在每日的“您好,脚本专家!”专栏中可以看到这个文件。

Figure 1 您好,脚本专家!XML 脚本

<?xml version="1.0" encoding="ISO-8859-1"?>
<Repository>
  <Script>
    <Category>Microsoft Office</Category>
    <Subcategory>Microsoft Access</Subcategory>
    <Keyword>databases</Keyword>
    <Title>How Can I Print a Microsoft Access Report?</Title>
    <URL>https://www.microsoft.com/technet/scriptcenter/resources/qanda/oct06/hey1020.mspx</URL>
  </Script>
  <Script>
    <Category>Microsoft Office</Category>
    <Subcategory>Microsoft Access</Subcategory>
    <Keyword>databases</Keyword>
    <Title>How Can I Compact a Microsoft Access Database?</Title>
    <URL>https://www.microsoft.com/technet/scriptcenter/resources/qanda/oct06/hey1009.mspx</URL>
  </Script>
  <Script>
    <Category>Microsoft Office</Category>
    <Subcategory>Microsoft Word</Subcategory>
    <Keyword>hyperlinks</Keyword>
    <Title>How Can I Change an Existing Hyperlink in a Microsoft Word Document?</Title>
    <URL>https://www.microsoft.com/technet/scriptcenter/resources/qanda/oct06/hey1016.mspx</URL>
  </Script>
  <Script>
    <Category>Enterprise Servers</Category>
    <Subcategory>Microsoft SQL Server</Subcategory>
    <Keyword>databases</Keyword>
    <Title>How Can I Create a Table in a SQL Server Database?</Title>
    <URL>https://www.microsoft.com/technet/scriptcenter/resources/qanda/oct06/hey1016.mspx</URL>
  </Script>
</Repository>

正如您所看到的,这是个非常简单的小文件。每个单独脚本均归于 <Script> 标记下。从现在开始,我们将这些脚本作为记录加以引用。是的,您说对了,XML 迷们决不会将这些脚本作为记录进行引用。不过没关系,我们这里的目的是为了向您展示,如何对新的和不同种类的数据源应用您已经了解的东西,即数据库查询技术。我们认为,首先应当集中于如何完成工作,然后以后或许会回到这个话题,并为您提供相应的术语。

好了,现在回过头来看看这些记录。每条记录中均包含以下字段:<Category>、<Subcategory>、<Keyword>、<Title> 和 <URL>。

嗯,您并不像我们想象的那样印象深刻。如果我们向您展示一个可打开此 XML 文件并回显以下内容的脚本,可能会有所帮助:

Set xmlDoc = CreateObject("Microsoft.XMLDOM")
xmlDoc.Async = "False"
xmlDoc.Load("C:\Scripts\Scripts.xml")

Set colNodes = xmlDoc.selectNodes _
("/Repository/Script/*")

For Each objNode in colNodes
   Wscript.Echo objNode.Text
Next

千万注意:这些标记名称区分大小写。这里是“/Repository/Script”,而不是“/repository/script”或“/REPOSITORY/SCRIPT”。

当然,这也并不是特别令人印象深刻。但关键在于:这一切并不像您可能预期的那么难。

在代码中您可以看到,我们一开始便创建了一个 Microsoft.XMLDOM 对象(用于与 XML 文件一同使用的 COM 对象)的实例。之后,我们将 Async 属性设置为 False;这有助于确保在控件返回脚本之前将整个文件读入内存中。然后使用 Load 方法读取文件 C:\Scripts\Scripts.xml:

xmlDoc.Load("C:\Scripts\Scripts.xml")

一旦文件载入内存中,我们便可使用 selectNodes 方法从数据库中选择指定的记录:

Set colNodes = xmlDoc.selectNodes _
("/Repository/Script/*")

仔细看看传递给 selectNodes 的参数。原来,这代表 XML 文件中的路径。我们的 XML 文件具有以下结构:

<Repository>
    <Script>
        <Category></Category>
        <Subcategory></Subcategory>
        <Keyword></Keyword>
        <Title></Title>
        <URL></URL>
    </Script>
</Repository>

您是否应该加以关注?一句话,是!处理 XML 文件时,结构十分重要。例如,我们如何来影响某个记录的各属性呢?很简单:我们访问后面跟有各属性标记的 <Script> 标记之前的 <Repository> 标记即可。猜猜看?这与我们作为参数指定给 selectNodes 的路径相同。我们只不过是在该路径后加了 /*,这意味着“选择这些记录的所有属性”。换言之,我们返回的集合将由数据库中所有记录的所有属性组成,类似于 Windows® Management Instrumentation (WMI) 中的“Select * From”查询。

注意,用该脚本试试,您便会了解我们的真正用意所在。

脚本的其余部分很简单,我们所需做的只是设置一个 For Each 循环遍历此集合,然后回显各项和各属性的 Text 属性值:

For Each objNode in colNodes
   Wscript.Echo objNode.Text
Next

比追汽车更容易的方法。

但您说对了:如果我们只是要回显该文件的整个内容,那么可以绕过所有的 XML 内容,只需使用 FileSystemObject 即可。(这可能有点儿复杂,但仅仅复杂一点儿而已。)如果我们的意图使您迷惑,并使您认为“哇,XML 内容还是相当便利的”,那么,我们仍有很长一段路要走。

接下来,让我们看看可以对此做些什么。在第一个脚本中,我们返回了所有记录的所有属性值。这很好,但假设我们真正要返回的是脚本的标题,能做到吗?嗯,稍等片刻......

好了,照 Lucy 看来,我们要做的只是修改 selectNodes 命令,将关注的属性添加到该路径的结尾。换句话说:

    Set colNodes=xmlDoc.selectNodes _    ("/Repository/Script/Title")

我们发现,第一次返回所有属性的唯一原因是我们使用了 * 通配符。现在,我们只返回了 Title 属性。这是为什么?因为这是我们要返回的唯一属性值。

注意,看这里:XML 将为您提供精确的所需。Lucy,别不高兴,我们的确认为 XML 是人们最好的新朋友!

当然,您可返回多个属性。例如,此修改的命令将会返回 Title 和 URL 属性的值:

    Set colNodes=xmlDoc.selectNodes _    ("/Repository/Script/(Title | URL)")

无可否认,这语法看上去有点笨,但一旦您习惯了,这并不算太糟糕。开始之前,我们指定路径的母体部分,正如之前所做的那样:

    /Repository/Script/

然后加入一组括号,并在这些括号中指定要返回的属性(请注意,我们使用管道分隔符(| 字符)将各属性分开)。换句话说:

    (Title | URL)

正如我们前面所说的,它可能有点笨,但的确起作用。

而且,您不是只能返回两个属性,这没有限制。只要添加管道分隔符,您便可返回所有想要的信息。例如,以下命令将会返回三个属性(Title、URL 和 Keyword):

    Set colNodes=xmlDoc.selectNodes _    ("/Repository/Script/(Title | URL |          Keyword)")

酷吧?

是的,我们认为它很酷。你们这些家伙真是难以对付。不过,需要再次指出的是,您说得很对:目前我们所做的一切只是检索数据库中与所有记录相关的信息。不过,这没有什么不对。通常这恰恰是您想要做的。另一方面,很多时候也存在这种情况,即我们要检索的信息只是与这些记录中的一个子集相关。例如,或许我们只是想要返回 Keyword 等于 databases 的脚本的相关信息。您知道,可以使用类似如下的查询:

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Keyword = ‘databases’]")

同样,这语法有点儿奇怪。不过,从好的方面看来,此语法同样很简单直接。指定母体路径后,我们将“Where”子句(例如,where x = y)置于方括号中,类似如下:

/Repository/Script [Keyword = ‘databases’]

您可能已经想到了,我们的 Where 子句等式将告诉脚本“仅返回其中 Keyword 属性为 databases 的项”。请注意,databases 括在单引号中。这样做不但相当整洁,而且还非常必要。如果筛选术语括在双引号中,那么您在运行该脚本时会提示语法错误。

说到为什么?请记住,因为整个查询字符串 "/Repository/Script [Keyword = ‘databases’]" 被括在双引号中。

当然,筛选数据时您不必限于使用等号 (=)。可使用大于 (>) 或小于 (<) 符号,以及自己喜好的方式,小于或等于 (<=) 以及大于或等于 (>=) 符号。您还可以在任何符号之前加一个惊叹号使之相反。例如,此命令将会请求所有脚本,其中 Keyword 不等于 databases(请注意 !=):

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Keyword != ‘databases’]")

稍等一下:是谁说“当然,您可以设置筛选器,但我敢打赌,您无法设置筛选器并指定想要返回的属性”?当然可以:

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Keyword = ‘databases’]/Title")

如果运行正常,此命令将仅会返回所有 Keyword 为 databases 的脚本的 Title 属性。让我们看看它是否起作用:

    How Can I Print a Microsoft Access Report?
    How Can I Compact a Microsoft Access Database?
    How Can I Create a Table in a SQL Server     Database?

让我们看看在调用它之前,是否可以再添加一条其他命令。尽管有所进步,但返回的记录可能还是会比真正想要返回的记录多。毕竟,我们之前的命令将返回 Microsoft® Access® 脚本和 Microsoft SQL Server™ 脚本混合。(为什么呢?因为所有这些脚本的 Keyword 均使用 databases。)要是我们想要限制返回数据的脚本 Keyword 为 databases 并且 Subcategory 为 Microsoft SQL Server,那该怎么办?能否通过多重标准进行筛选呢?

看来您要问:

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Keyword = ‘databases’ and " & _
    "Subcategory = ‘Microsoft SQL Server’]")

其具有相同的基本语法;我们只不过使用了两个单独的条件(即,Keyword = ‘databases’ 和 Subcategory = ‘Microsoft SQL Server’),并使用 AND 运算符将这两个条件连接起来而已。

是的,您说对了:通过这个简单的 XML 小文件,我们可以只请求其 Subcategory = Microsoft SQL Server 的所有脚本。但这并不十分有趣,因为其根本不具指导意义。

您也可使用 OR 子句。例如,假设我们有一大批 Microsoft Office 脚本,其中包括子分类等于 Microsoft Excel®、Microsoft PowerPoint®、Microsoft Outlook® 等条件的脚本。能否限制仅返回来自子分类 Microsoft Word 或 Microsoft Access 的脚本数据呢?当然可以:

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Subcategory = ‘Microsoft Word’ " & _
    "or Subcategory = ‘Microsoft Access’]")

看上去是这个样子吗?诚然,本专栏可能永远没有资格成为一个可以改变您生活的专栏;不过以这种方式查询 XML 文件还是迟早可能会派上用场的。但决定权在于你们:您可以学习一些基本的 XML 技巧,也可以去追汽车。这由您来决定。(如果您选择后者,而又碰巧遇见 Lucy (沿着大街追汽车的那条狗),那么,请代脚本专家向它问好。还有告诉它不要呆在我们的院子里!)

您好,脚本专家!- 每天

您可能刚刚仔细读完本月的“您好,脚本专家!”专栏,并且认为它是您读过的最有收获的技术资料。您甚至可能会说这是迄今为止撰写的最好的资料。甚或,您正急切地徘徊于邮箱旁期盼着下一期的 TechNet 杂志,这样您便可读到更多类似于此的文章。

究竟您在等待什么?想知道更多关于 Lucy(邻居家的狗)的事,还是阅读有关“脚本小子”的最新成果呢?听说过去年的年度 Turducken 投球赛是如何过去的吗?请每天阅读(没错,我们说的是每天)“您好,脚本专家!”专栏,保持与这些重大事件齐头并进。每个星期一到星期五(除了重大的节假日和脚本专家的假期),您都可以读到所有关于中学棒球比赛、学院足球比赛和篮球比赛的赛况信息,偶尔也会看到当地的天气预报。(当然,这里所说的“本地”是指雷蒙德,但不想知道雷蒙德天气情况的人又该怎么办呢?)

不仅如此,实际上您每天还可以学到一些有关脚本的新知识。是的,所有如此吸引人的嵌入内容实际上是脚本信息。每篇专栏中,脚本专家都将回答他们认为是由真人提出的真实问题。您可以在线阅读每日的专栏,其网址为:microsoft.com/technet/scriptcenter/resources/qanda。同样,因为他们已经解答了很多问题,所以存档文件数量相当大,因而形成了巨大的脚本信息资源(请参阅:microsoft.com/technet/scriptcenter/resources/qanda/hsgarch.mspx)。

有问题要问脚本专家吗?您可以将问题提交给 scripter@microsoft.com,有少许机会可以得到回答。(相比之下,如果您不提交问题的话,您就没有机会得到回答,所以提交有什么损失呢?这不像彩票,这可是免费的,并且比中彩票的机会稍大一些。)

Microsoft 脚本专家为 Microsoft 工作,也就是说受雇于 Microsoft。在没有玩/指导/观看棒球(及其他各种娱乐活动)时,他们会管理 TechNet 脚本中心。要查找所需信息,请登录网站 www.scriptingguys.com

© 2008 Microsoft Corporation 与 CMP Media, LLC.保留所有权利;不得对全文或部分内容进行复制.