XML无处不在。尽管它使用了烦人的尖括号,但XML格式仍然被广泛使用。配置文件、RSS订阅、Office文件(.docx中的“x”)只是部分列表。使用PowerShell解析XML文件是您PowerShell之旅中的重要一步。
本教程将向您展示如何使用PowerShell解析XML文件并验证其有效性。这将带您从零基础到精通获取和评估XML数据的各个方面。您将获得工具,帮助您验证XML数据的完整性,并在脚本门前阻止错误数据的进入!
先决条件
要跟随所呈现的材料,您应具备:
- PowerShell版本3.0及以上。示例是在Windows PowerShell v5.1上创建的。
- Notepad++、Visual Studio Code或其他理解XML的文本编辑器。
使用Select-Xml
解析PowerShell XML元素
首先让我们来介绍使用PowerShell解析XML的最流行和最简单的方法之一,那就是使用Select-Xml
。 Select-Xml
cmdlet允许您提供一个XML文件或字符串,以及一个称为XPath的“过滤器”,从中提取特定信息。
XPath是一系列元素名称的链。它使用“路径”样式的语法来识别和导航XML文档中的节点。
假设您有一个带有许多计算机的XML文件,并希望使用PowerShell解析此XML文件。每台计算机都有诸如名称、IP地址和Include
元素(用于报告中的包含)等各种元素。
元素是具有开头标签和结束标签的XML部分,中间可能包含一些文本,比如
<Name>SRV-01</Name>
您希望使用PowerShell解析此XML文件以获取计算机名称。为此,您可以使用Select-Xml
命令。
在上面的文件中,计算机名称出现在Name元素的InnerXML中。
InnerXML是两个元素标签之间的文本。
要查找计算机名称,首先需要提供适当的XPath(/Computers/Computer/Name
)。此XPath语法将仅返回Computer
元素下的Name
节点。然后,为了仅获取每个Name
元素的InnerXML,请使用Node.InnerXML
属性并使用ForEach-Object
循环。
使用PowerShell解析带有Select-Xml
的XML属性
现在让我们从不同的角度来解决这个找到计算机名称的问题。这一次,与XML 元素代表的计算机描述符不同,它们是用XML 属性表示的。
一个属性是一个键/值部分,例如
name="SRV-01"
。属性总是出现在开标签内,在标签名之后。
下面是用属性表示的计算机描述符的XML文件。现在你可以把每个描述符看作是一个属性,而不是一个元素。
由于这次每个描述符都是一个属性,所以稍微调整一下XPath,只找到计算机元素。然后,再次使用ForEach-Object
cmdlet,找到name属性的值。
确实,这也带来了相同的结果:SRV-01,SRV-02和SRV-03:

在这两种情况下,无论你是读取元素还是属性,Select-Xml
的语法都很麻烦:它强迫你使用XPath
参数,然后将结果传递给一个循环,最后在Node属性下查找数据。
幸运的是,PowerShell提供了一种更方便、更直观的方法来读取XML文件。PowerShell允许你读取XML文件并将其转换为XML对象。
相关内容: 使用PowerShell数据类型加速器加速编码
将XML字符串转换为对象
另一种使用 PowerShell 解析 XML 的方法是将 XML 转换为对象。这样做的最简单方法是使用 [xml]
类型加速器。
通过在变量名前加上 [xml]
,PowerShell 将原始纯文本 XML 转换为您可以使用的对象。
读取 XML 对象元素
现在,$xmlElm
和 $xmlAttr
变量都是 XML 对象,允许您通过点表示法引用属性。也许您需要找到每个计算机元素的 IP 地址。由于 XML 文件是一个对象,因此您可以通过简单地引用 IP 元素来实现。
从 PowerShell 版本 3.0 开始,XML 对象使用相同的语法获取属性值,用于读取元素的内部文本。因此,IP 地址的值是使用与元素文件完全相同的语法从属性文件中读取的。
读取 XML 属性
使用完全相同的点表示法,您也可以读取 XML 属性,尽管 XML 结构有所不同。
下面的结果显示,两者都获得了相同的数据,每个文件都来自其相应的文件:

此外,使用对象后,一旦加载到内存中,即使您正在使用 PowerShell ISE,您也可以获得 IntelliSense 来进行制表符补全。例如,如下所示。

遍历 XML 数据
一旦您绕过直接读取 XML 文件到 XML 对象(利用 [xml]
类型加速器的优势),您就拥有了 PowerShell 对象的全部功能。
假设,例如,您需要循环遍历出现在 XML 文件中具有 include=”true” 属性的所有计算机,以检查它们的连接状态。下面的代码显示了如何实现这一点。
这个脚本是:
- 读取文件并将其转换为 XML 对象。
- 遍历相关计算机以获取它们的连接状态。
- 最后,将结果输出对象发送到管道。
上述脚本的结果如下所示:

XML 模式
在前一节中,您看到了两种不同的 XML 文件以两种不同的方式表示数据集。这些方式称为 XML 模式。XML 模式定义了 特定 XML 文档的合法构建块:
- 可以出现在该特定文档中的元素和属性的名称。
- 子元素的数量和顺序。
- 元素和属性的数据类型。
模式本质上定义了 XML 的结构。
验证 XML 数据
XML文件可能具有正确的语法(像Notepad++这样的编辑器会发出警告),但其数据可能不符合项目要求。这就是模式(schema)发挥作用的地方。当您依赖XML数据时,必须确保所有数据根据定义的模式是有效的。
您不希望发现运行时数据错误,这可能发生在脚本中间500行的深处。那时候,它可能已对文件系统和注册表执行了一些不可逆转的操作。
那么,如何提前检查数据是否正确呢?让我们首先看一些可能的错误类型。
XML数据可能的错误
一般来说,XML文件中发现的错误属于两类:元数据错误和数据本身的错误。
XML元数据错误
下面的文件MorePeople.xml在语法上是完全有效的。您可以看到文件中有一个单独的People元素(根元素),其中包含三个Person元素。这种结构是完全可以接受的。但是,它包含一个异常,您能看到吗?
如果您没有看到,不要担心,它只是隐藏起来了。问题出现在第一个内部元素中:
本应该是Country,但被拼错了,Canada 被降级为County。
XML数据中的错误
在修复了MorePeople.xml上的Country问题后,另一个问题悄悄地潜入了:
元数据,即元素和属性,都很好。那问题出在哪里?这次问题仍然出在第一个Person 行的值中的其中一个。有人决定yes是true的足够替代——但是像下面这样的代码将无法获得第一个元素,因为它正在寻找true,而不是yes:
创建 XML 模式
现在您知道可能出现的错误类型了,是时候展示一下模式文件如何帮助解决问题了。第一步是创建一个样本数据文件。这个样本可以是最小的示例,仅包含一个内部元素。对于上面的示例,让我们创建一个像这样的样本文件People.xml:
现在,在下面构建一个PowerShell 函数并使用它与样本数据一起创建.xsd 模式文件。
将该函数复制到您的 ISE 或您喜欢的 Powershell 编辑器中,将其加载到内存中,并使用它创建模式。加载了函数后,用于创建模式的代码是这个一行代码:
结果将显示新创建的模式的路径:

使用模式文件验证您的数据
看一下上面结果指定的位置。如果.xsd文件存在,你正在正确的路径上查看验证的过程。在确认步骤中,请使用以下函数:
将函数加载到内存中,并使用它来验证两个错误示例中的MorePeople.xml。要触发验证,请使用以下命令:
实际结果取决于MorePeople.xml的内容。
让我们看两个例子。请注意,当MorePeople.xml没有错误时,上面的函数将返回True
。

当MorePeople.xml文件包含错误的数据(Country键拼写错误为County)时,函数将返回一些失败细节并返回False
。

正如你所看到的,详细输出中指定的错误信息非常有信息量:它指向问题文件并指出了其中出现问题的确切组件。
调整验证模式
让我们看一下模式文件,然后看看我们如何让它更好。
New-XmlSchema
默认创建的模式如下:
上面的默认模式很好,但不是完美的。确实,它捕捉到了Country属性的拼写错误。但是,如果保留模式不变,如果未满足其他期望,这些问题将不会被Test-XmlBySchema
验证报告为错误。让我们解决这个问题。
下表列出了一些未被视为验证错误并且将被Test-XmlBySchema
忽略的情况。在每一行中,右侧的列显示了如何手动更改模式以添加对所需保护的支持。
A modified version of the schema file, with all protections added, is shown right after the table.
添加到默认模式的设置 – 示例
Required Behavior | Relevant setting on the schema file |
At least one Person element | Set the minOccurs value of the Person element to 1 |
Make Name, Country and IsAdmin attributes mandatory | Add use=”required” to each of these attributes declaration |
Allow only true/false values for IsAdmin | Set type=”xs:boolean” for the IsAdmin declaration |
Allow only Country names between 3 and 40 characters | Use the xs:restriction (see detailed explanation after the schema text) |
在示例中,对IsAdmin属性进行布尔限制,其值必须为小写 true或false。
使用xs:restriction进行字符串长度验证
字符串长度验证有点复杂。因此,即使它如上所示作为修改后模式的一部分,它也值得更多关注。
Country属性的原始模式项(在手动添加use=”required”后)如下所示:
要添加长度保护,您应添加<xs:simpleType>
元素,并在其中添加<xs:restriction base="xs:string">
。这个限制又包含了在xs:minLength
和xs:minLength
上声明的必要限制。
在所有这些更改之后,最终的xs:attribute
声明已从单行变成了一个巨大的8行节点:
如果在上面的解释之后您的头脑没有眩晕,您有权看到验证的实际操作。要做到这一点,让我们故意将值Canada缩短为两个字符音节:Ca
使用简短的国家名称,并保存MorePeople.xml,您就可以运行以下验证命令:
而且结果确实显示复杂的模式已经发挥了作用:

XML模式验证可能会变得非常复杂,并验证您能想到的几乎任何模式,特别是当与正则表达式结合使用时。