65.9K
CodeProject 正在变化。 阅读更多。
Home

PowerShell 和 XML

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (9投票s)

2010年2月27日

CPOL

6分钟阅读

viewsIcon

280255

介绍 PowerShell 如何轻松地以编程方式处理 XML

引言

XML无处不在。因此,许多人发现需要处理XML,而传统的文本编辑器无法胜任。有些编辑器提供不错的格式化功能(如 Notepad++),但它们不提供以编程方式检查XML底层数据的机制。

PowerShell使处理XML变得非常简单。它将XML元素转换为.NET对象的属性,无需编写任何解析代码。所以您只需要在计算机上安装PowerShell即可开始!

快速示例

这是一个快速示例,展示PowerShell如何将XML元素和属性映射到对象属性。它假设file.xml文件存在并包含以下文本:

<!-- file.xml -->
<employees>
	<employee id="101">
		<name>Frankie Johnny</name>
		<age>36</age>
	</employee>
	<employee id="102">
		<name>Elvis Presley</name>
		<age>79</age>
	</employee>
	<employee id="301">
		<name>Ella Fitzgerald</name>
		<age>102</age>
	</employee>
</employees>

加载file.xmlXmlDocument 对象并访问节点/属性的示例。

PS C:\> $xml = [xml](get-content file.xml)
PS C:\> $xml

#comment                                                    employees
--------                                                    ---------
 file.xml                                                   employees

PS C:\> $xml.employees

employee
--------
{Frankie Johnny, Elvis Presley, Ella Fitzgerald}

PS C:\> $xml.employees.employee

id                                      name                                    age
--                                      ----                                    ---
101                                     Frankie Johnny                          36
102                                     Elvis Presley                           79
301                                     Ella Fitzgerald                         102

PS C:\> $xml.employees.employee[0].name
Frankie Johnny
PS C:\> $xml.employees.employee[1].age
79
PS C:\>

cmdlet `get-content`相当于UNIX中的`cat`,它以文本行的形式返回文件内容。

`get-content` cmdlet前面的方括号`[xml]`表示一个类型对象。在这种情况下,它将从`get-content file.xml`返回的文本转换为XmlDocument 对象。一旦您拥有一个XmlDocument 对象,PowerShell内置的XML支持就会生效。单个XMLElement 对象将其子节点作为属性呈现。这意味着元素名称就是属性。在这种情况下,根元素是<employees>,因此它作为属性在`$xml`变量上进行访问。试试这个:输入'`$xml.em`'然后按TAB键。是的,元素名称支持Tab自动完成。这让您更容易一些。

等等,还有更多

您仍然可以访问底层XmlElement 对象的各种方法。执行命令'`$xml | gm`'以获取任何XML节点可用的所有方法和属性列表。这意味着您可以使用XPath 查询语法调用SelectNodes() SelectSingleNode() 。以下是使用XmlElement上的SelectNodes()方法的示例。

PS C:\> $xml = (Get-Content file.xml)
PS C:\> $xml = [xml](Get-Content file.xml)
PS C:\> $xml.SelectNodes("/employees/employee")

id                                      name                                    age
--                                      ----                                    ---
101                                     Frankie Johnny                          36
102                                     Elvis Presley                           79
301                                     Ella Fitzgerald                         102

通过将这些结果通过PowerShell管道传递,您可以将这些结果传递给其他命令,如select-object、where-object和foreach-object,以修剪值或基于值执行命令,从而获得非常富有表现力和强大的脚本体验。**注意**:我使用的是`get-content` (gc) 的别名。大多数PowerShell cmdlet都有其全名的缩写别名。

在下面的4个示例中,我们使用where-object和foreach-object来检查employee节点的属性,以便我们只返回符合特定条件的结果或以某种方式处理它们。第一个示例看起来应该返回年龄大于50的2个员工,但它并没有。原因是XML对象的属性始终是字符串。因此,它进行的是字典序比较。这可以通过使用类型转换运算符`[int]`将值转换为int 来轻松解决,这与我们将`get-content`的字符串输出转换为xml文档`[xml]`的方式非常相似。

第2个命令显示,转换为int 后可以正确返回年龄大于50的员工。第3个示例显示您可以调用返回的string 对象上的方法。因此,假设您的员工ID的第一个数字表示部门,您可以使用String 对象上的startsWith() 方法轻松实现。`$_`是什么?`$_`是一个代表当前管道对象的变量。使用它是为了让您能够操作传递给函数/脚本块的当前对象。where-object cmdlet接受一个脚本块(由大括号`{ }`表示),并执行其中的powershell脚本。如果表达式评估为true,则对象将通过管道传递到下一个命令(在本例中是默认的输出命令);当where-object脚本块评估为false时,对象将被“丢弃”。

如果您想以某种方式处理XML节点的值,可以使用foreach-object,它也接受一个脚本块。事实上,第4个命令使用foreach 来创建所有员工节点值的连接字符串。

PS C:\> $xml = [xml](gc file.xml)
PS C:\> $xml.employees.employee | where { $_.age -gt 50 }

id                                      name                                    age
--                                      ----                                    ---
102                                     Elvis Presley                           79

PS C:\> $xml.employees.employee | where { [int]$_.age -gt 50 }

id                                      name                                    age
--                                      ----                                    ---
102                                     Elvis Presley                           79
301                                     Ella Fitzgerald                         102

PS C:\> $xml.employees.employee | where { $_.id.startsWith("1") }

id                                      name                                    age
--                                      ----                                    ---
101                                     Frankie Johnny                          36
102                                     Elvis Presley                           79

PS C:\> $xml.employees.employee | foreach { $_.id + ":" + $_.name + ":" + $_.age }
101:Frankie Johnny:36
102:Elvis Presley:79
301:Ella Fitzgerald:102

一个非常简单愚蠢的例子

RSS feed易于访问,它们基本上就是XML。所以让我们看看PowerShell如何处理RSS feed。互联网上已经有很多文章展示了这一点有多么容易,但我认为我应该也包含进来。从您喜欢的网站抓取一个RSS feed,看看是否有任何文章符合某些条件。我将抓取TMZ的RSS feed,并检查是否有包含我的名字“scott”的文章。首先,我想展示如何抓取XML RSS文档并显示文章,使用其一个或多个属性。这可以通过使用.NET类System.Net.WebClientDownloadString(String url)方法来实现。非常容易。

接下来,我将选择标题中包含“scott”的文章。我将使用-imatch Regex Powershell运算符。它对目标字符串在输入字符串中的任何位置进行不区分大小写的搜索。**注意**:您的结果可能不同,因为TMZ网站上的文章每天都在变化。另外,我使用的是'ft',它是'format-table'的别名,它允许我选择要显示的属性。

PS C:\powershell> $url = "http://www.tmz.com/rss.xml"
PS C:\powershell> $feed=[xml](new-object system.net.webclient).downloadstring($url)
PS C:\powershell> $feed.rss.channel.item | format-table title,link

title                                                       link
-----                                                       ----
Report: Marie Osmond's Son Commits Suicide                  http://www.tmz.com/2010/02/27/marie-osmond-son-commits-s...
Scotty Lago Conspiracy Theory -- Up in Smoke                http://www.tmz.com/2010/02/27/scotty-lago-michael-phelps...
Brittany Murphy -- 109 Mystery Pills                        http://www.tmz.com/2010/02/27/brittany-murphy-prescripti...
Liev Schreiber to the Rescue!                               http://www.tmz.com/2010/02/27/liev-schreiber-broadway-au...
Scotty Lago's Olympic Conspiracy Theory                     http://www.tmz.com/2010/02/27/scotty-lago-olympics-vanco...
Britney Spears -- The Blonde Is Back                        http://www.tmz.com/2010/02/27/britney-spears-blonde-hair...
Former 'Idol' Elliott Yamin in Chile During Quake           http://www.tmz.com/2010/02/27/american-idol-elliott-yami...
'Pants on the Ground' Guy -- King of Vegas                  http://www.tmz.com/2010/02/27/pants-on-the-ground-guy-la...
Reality to Nas -- 'Memba Me?                                http://www.tmz.com/2010/02/27/nas-federal-tax-bill-lien-...
Nic Cage's Manager -- Get Me Outta This Suit!               http://www.tmz.com/2010/02/27/nic-cage-manager-sam-levin...
Audrina Patridge:  Bad Acting Got Me Towed                  http://www.tmz.com/2010/02/27/audrina-patridge-tow-car-t...
TMZ's Bangin' Backside Contest -- Bootyfull!                http://www.tmz.com/2010/02/27/tmzs-bangin-backside-conte...
Conan the Barbarian -- One Hairy Situation                  http://www.tmz.com/2010/02/27/conan-the-barbarian-in-ano...
Carol Brady vs. Mrs. C: Who'd You Rather?                   http://www.tmz.com/2010/02/27/carol-brady-vs-mrs-c-whod-...
Nicole Richie & the Chocolate Factory                       http://www.tmz.com/2010/02/27/nicole-richie-and-the-choc...
Guess Who This Guy Turned Into!                             http://www.tmz.com/2010/02/27/guess-who-this-guy-turned-...
Jon Cryer Alleged Hit - Mexico Gang Connection?             http://www.tmz.com/2010/02/27/jon-cryer-alleged-hit-mexi...
Avril & Deryck's Divorce Takes a Backseat                   http://www.tmz.com/2010/02/27/avril-lavigne-and-derycks-...
World to Joanna Krupa's Mom -- Thank You!                   http://www.tmz.com/2010/02/27/joanna-krupa-dancing-with-...
K-Fed Confused by Green Mystery Substance                   http://www.tmz.com/2010/02/27/k-fed-kevin-federline-shop...

PS C:\powershell> $feed.rss.channel.item | where { $_.title -imatch "scott" } | ft title,link

title                                                       link
-----                                                       ----
Scotty Lago Conspiracy Theory -- Up in Smoke                http://www.tmz.com/2010/02/27/scotty-lago-michael-phelps...
Scotty Lago's Olympic Conspiracy Theory                     http://www.tmz.com/2010/02/27/scotty-lago-olympics-vanco...

细则和注意事项

“item”属性问题

Powershell会自动为每个XmlElement添加一个“Item”属性。这使得可以像哈希表一样访问其属性,如'`$feed["rss"]`'。由于RSS文档包含'Item'节点,PowerShell在尝试访问它们时会报错。所以您必须通过更改名称(我知道这很烦人)或编写代码来绕过“item”节点,就像上面的示例一样。下面是一个在尝试访问底层“Item”节点时看到的错误的示例。

PS C:\powershell> $feed.rss.channel
format-default : The member "Item" is already present.
    + CategoryInfo          : NotSpecified: (:) [format-default], ExtendedTypeSystemException
    + FullyQualifiedErrorId : 
    AlreadyPresentPSMemberInfoInternalCollectionAdd,Microsoft.PowerShell.Commands.FormatDefa
   ultCommand

隐藏方法

XmlDocument XmlElement 的一些底层方法并未通过`get-member`公开。但您可以从.NET API文档中轻松找到它们。并且类定义的任何属性都需要使用它们的get方法形式(`get_Property()`)来访问。建议了解底层XmlElement 实例的方法或随身携带API文档。

更新XML

PowerShell中没有设置XmlElements 值的本机支持。您必须像在C#中一样调用底层XmlElement/XmlDocument 对象的get方法。但请记住,当您执行`'$xml | gm'`时它们不会显示。了解XML .NET类(XmlElementXmlDocument)的方法在这里很有帮助。

其他资源

描述“item”属性问题的文章,以及一些关于如何在PowerShell中修改XML文档并将其保存回来的示例

后续步骤

在我目前的工作中,我正在使用Lucene为我们的音乐目录(包括专辑、曲目和艺术家)构建搜索索引。Lucene是一个开源的信息检索库。它被广泛使用,并且有Java版本和.NET端口。我们在这里工作时使用Java,但我一直在PowerShell中编写一些围绕Lucene.NET的脚本。不幸的是,Lucene.NET端口比Java版本落后几个版本,但仍然非常有用(最新版本是2.4)。在我的下一篇文章中,我计划结合PowerShell强大的XML处理功能,无缝集成Lucene.NET,以便可以非常轻松地从网上获取XML文档/RSS feed,并对它们进行索引以实现快速检索。

结论

希望这篇文章展示了使用Windows PowerShell处理XML的简便性。除了强大的XML处理能力之外,我希望我还展示了如何将管道架构与XML结合起来,创建非常灵活且有用的PowerShell脚本/工具,从而提高表达能力。

这是我第一次在CodeProject发帖。我希望您觉得它有用,并欢迎任何反馈。谢谢!

历史

  • 2010年2月27日:初始帖子
© . All rights reserved.