当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] PowerShell 解析、读取和验证 XML 的魔力

作者:精品下载站 日期:2024-12-14 13:04:24 浏览:15 分类:玩电脑

PowerShell 解析、读取和验证 XML 的魔力


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-XmlSelect-Xml cmdlet 允许您提供 XML 文件或字符串以及称为 XPath 的“过滤器”,以提取特定信息。

XPath 是一个元素名称链。它使用“类似路径”语法来识别和导航 XML 文档中的节点。

假设您有一个包含多台计算机的 XML 文件,并且希望使用 PowerShell 来解析该 XML 文件。每台计算机都有各种元素,例如名称、IP 地址和用于包含在报告中的 Include 元素。

元素是带有开始标记和结束标记的 XML 部分,中间可能有一些文本,例如 SRV-01

<Computers>
	<Computer>
		<Name>SRV-01</Name>
		<Ip>127.0.0.1</Ip>
		<Include>true</Include>
	</Computer>	
	<Computer>
		<Name>SRV-02</Name>
		<Ip>192.168.0.102</Ip>
		<Include>false</Include>
	</Computer>	
	<Computer>
		<Name>SRV-03</Name>
		<Ip>192.168.0.103</Ip>
		<Include>true</Include>
	</Computer>	
</Computers>

您想使用 PowerShell 解析此 XML 文件以获取计算机名称。为此,您可以使用 Select-Xml 命令。

在上面的文件中,计算机名称出现在 Name 元素的内部文本 (InnerXML) 中。

InnerXML 是两个元素标签之间的文本。

要查找计算机名称,您首先需要提供适当的 XPath (/Computers/Computer/Name)。此 XPath 语法将仅返回 Computer 元素下的 Name 节点。然后,要仅获取每个 Name 元素的 InnerXML,请使用 ForEach-Object 循环访问每个元素的 Node.InnerXML 属性。

Select-Xml -Path C:\Work\computers-elm.xml -XPath '/Computers/Computer/Name' | ForEach-Object { $_.Node.InnerXML }

使用 PowerShell 通过 Select-Xml 解析 XML 属性

现在让我们从不同的角度解决查找计算机名称的问题。这次,计算机描述符不再用 XML 元素 表示,而是用 XML 属性 表示。

属性是键/值部分,例如 name="SRV-01" 。属性始终出现在开始标记内,位于标记名称之后。

下面是带有用属性表示的计算机描述符的 XML 文件。您现在可以将每个描述符视为属性而不是元素。

<Computers>
	<Computer name="SRV-01" ip="127.0.0.1" include="true" />
	<Computer name="SRV-02" ip="192.168.0.102" include="false" />
	<Computer name="SRV-03" ip="192.168.0.103" include="true" />
</Computers>

由于这次每个描述符都是一个属性,因此请稍微调整 XPath 以仅查找 Computer 元素。然后,再次使用 ForEach-Object cmdlet 查找 name 属性的值。

Select-Xml -Path C:\Work\computers-attr.xml -XPath '/Computers/Computer' | ForEach-Object { $_.Node.name }

事实上,这也带来了相同的结果:SRV-01SRV-02SRV-03:

[玩转系统] PowerShell 解析、读取和验证 XML 的魔力

在这两种情况下,无论您是读取元素还是属性,Select-Xml 的语法都很麻烦:它强制您使用 XPath 参数,然后将结果通过管道传递给一个循环,最后查找 Node 属性下的数据。

幸运的是,PowerShell 提供了一种更方便、更直观的方式来读取 XML 文件。 PowerShell 允许您读取 XML 文件并将其转换为 XML 对象

相关:使用 PowerShell 数据类型加速器加速编码

将 XML 字符串转换为对象

使用 PowerShell 解析 XML 的另一种方法是将 XML 转换为对象。最简单的方法是使用 [xml] 类型加速器。

通过在变量名称前添加 [xml] 前缀,PowerShell 会将原始纯文本 XML 转换为您可以使用的对象。

[xml]$xmlElm = Get-Content -Path C:\Work\computers-elm.xml
[xml]$xmlAttr = Get-Content -Path C:\Work\computers-attr.xml

读取 XML 对象元素

现在,$xmlElm$xmlAttr 变量都是 XML 对象,允许您通过点表示法引用属性。也许您需要找到每个计算机元素的 IP 地址。由于 XML 文件是一个对象,因此您可以通过简单地引用 IP 元素来实现。

$xmlElm.Computers.Computer.ip

从 PowerShell 3.0 版开始,XML 对象使用与读取元素内部文本相同的语法来获取属性值。因此,IP 地址的值是从属性文件中读取的,其语法与元素文件完全相同。

读取 XML 属性

使用完全相同的点表示法,您也可以读取 XML 属性,尽管 XML 结构存在差异。

$xmlElm.Computers.Computer.ip
$xmlAttr.Computers.Computer.ip

下面的结果表明两者都获得了相同的数据,每个数据都来自其相应的文件:

[玩转系统] PowerShell 解析、读取和验证 XML 的魔力

此外,对于该对象,一旦将其加载到内存中,如果您使用的是 PowerShell ISE,您甚至可以使用 IntelliSense 进行制表符补全。例如如下所示。

[玩转系统] PowerShell 解析、读取和验证 XML 的魔力

迭代 XML 数据

一旦您成功将 XML 文件直接读取为 XML 对象(通过利用 [xml] 类型加速器),您就拥有了 PowerShell 对象的全部功能。

例如,您需要循环遍历 XML 文件中包含 include=”true” 属性的所有计算机,以检查它们的连接状态。下面的代码展示了如何做到这一点。

这个脚本是:

  • 读取文件并将其转换为 XML 对象。
  • 迭代相关计算机以获取其连接状态。
  • 最后将结果输出对象发送到管道。
 ## casting the file text to an XML object
 [xml]$xmlAttr = Get-Content -Path C:\Work\computers-attr.xml

 ## looping through computers set with include="true"
 $xmlAttr.Computers.Computer | Where-Object include -eq 'true' |  ForEach-Object {
     ## see if the current computer is online
     if(Test-Connection -ComputerName $_.ip -Count 1 -Quiet)
     {
         $status = 'Connection OK'
     }
     else
     {
         $status = 'No Connection'
     }

     ## output the result object
     [pscustomobject]@{
         Name = $_.name
         Ip = $_.ip
         Status = $status
     }
 }

上面脚本的结果如下所示:

[玩转系统] PowerShell 解析、读取和验证 XML 的魔力

XML模式

在上一节中,您看到了两个不同的 XML 文件以两种不同的方式表示数据集。这些方式称为 XML 模式。 XML 模式定义特定 XML 文档的合法构建块:

  • 可以出现在该特定文档中的元素和属性的名称。
  • 子元素的数量和顺序。
  • 元素和属性的数据类型。

该模式本质上定义了 XML 的结构。

验证 XML 数据

XML 文件可能具有正确的语法(如果语法不正确,Notepad++ 等编辑器会抱怨),但其数据可能与项目要求不匹配。这就是模式发挥作用的地方。当您依赖 XML 数据时,您必须确保所有数据根据定义的架构都是有效的。

您最不想看到的就是在运行时发现脚本中间 500 行深处的数据错误。到那时,它可能已经对文件系统和注册表执行了一些不可逆的操作。

那么,如何提前检查数据是否正确呢?让我们首先看看一些可能的错误类型。

XML 数据中可能出现的错误

一般来说,在 XML 文件上发现的错误属于以下两类之一:元数据错误和数据本身的错误。

XML 元数据错误

下面的这个文件 MorePeople.xml 在语法方面是完全有效的。您可以在下面看到该文件有一个 People 元素(根元素),其中包含三个 Person 元素。这种结构是完全可以接受的。尽管如此,它还是有一个例外,你能看到吗?

<People>
	<Person Name="Debra" County="Canada" IsAdmin="true" />
	<Person Name="Jacob" Country="Israel" IsAdmin="true" />
	<Person Name="Olivia" Country="Cyprus" IsAdmin="false" />
</People>

如果没有,请不要担心,它只是隐藏起来了。问题出现在第一个内部元素中:

本来应该是一个国家的地方被拼写错误,加拿大被降级为

XML 数据中的错误

修复了 MorePeople.xml 上的 Country 问题后,又出现了另一个问题:

<People>
	<Person Name="Debra" Country="Canada" IsAdmin="yes" />
	<Person Name="Jacob" Country="Israel" IsAdmin="true" />
	<Person Name="Olivia" Country="Cyprus" IsAdmin="false" />
</People>

元数据,即元素和属性,都很好。那么有什么问题吗?这次,问题再次发生在第一行 Person 上,出现在其中一个中。有人认为 yes 足以替代 true - 但像下面这样的代码将无法获取第一个元素,因为它正在寻找 true ,不适合

$peopleXml.People.Person | Where-Object IsAdmin -eq 'true'

创建 XML 架构

现在您已经知道可能发生的错误类型,现在是时候展示架构文件如何提供帮助了。第一步是创建示例数据文件。该样本可以是最小的示例,并且只包含单个内部元素。对于上面的示例,让我们创建一个如下所示的示例文件 People.xml

<People>
	<Person Name="Jeff" Country="USA" IsAdmin="true" />
</People>

现在在下面构建一个 PowerShell 函数,并将其与示例数据一起使用来创建 .xsd 架构。

function New-XmlSchema
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory)]
        [ValidateScript({ Test-Path -Path $_ })]
        [ValidatePattern('\.xml')]
        [string]$XmlExample
    ) 

    $item = Get-Item $XmlExample
    $dir = $item.Directory
    $name = $item.Name
    $baseName = $item.BaseName

    ## build the schema path by replacing '.xml' with '.xsd'
    $SchemaPath = "$dir$baseName.xsd"

    try
    {
        $dataSet = New-Object -TypeName System.Data.DataSet
        $dataSet.ReadXml($XmlExample) | Out-Null
        $dataSet.WriteXmlSchema($SchemaPath)
        
        ## show the resulted schema file
        Get-Item $SchemaPath
    }
    catch
    {
        $err = $_.Exception.Message
        Write-Host "Failed to create XML schema for $XmlExample`nDetails: $err" -ForegroundColor Red
    }
}

将该函数复制到您的 ISE 或您最喜欢的 Powershell 编辑器,将其加载到内存中并使用它来创建架构。加载函数后,创建模式的代码如下:

New-XmlSchema -XmlExample 'C:\Work\People.xml'

结果将显示新创建的架构的路径:

[玩转系统] PowerShell 解析、读取和验证 XML 的魔力

使用架构文件验证您的数据

看一下上面结果指定的位置。如果 .xsd 存在,那么您就可以正确地查看正在执行的验证。对于确认步骤,请使用以下函数:

function Test-XmlBySchema
{
    [CmdletBinding()]
    [OutputType([bool])]
    param
    (
        [Parameter(Mandatory)]
        [ValidateScript({ Test-Path -Path $_ })]
        [ValidatePattern('\.xml')]
        [string]$XmlFile,
        [Parameter(Mandatory)]
        [ValidateScript({ Test-Path -Path $_ })]
        [ValidatePattern('\.xsd')]
        [string]$SchemaPath
    )

    try
    {
        [xml]$xml = Get-Content $XmlFile
        $xml.Schemas.Add('', $SchemaPath) | Out-Null
        $xml.Validate($null)
        Write-Verbose "Successfully validated $XmlFile against schema ($SchemaPath)"
        $result = $true
    }
    catch
    {
        $err = $_.Exception.Message
        Write-Verbose "Failed to validate $XmlFile against schema ($SchemaPath)`nDetails: $err"
        $result = $false
    }
    finally
    {
        $result
    }
}

将函数加载到内存,并使用它来验证两个错误示例中的 MorePeople.xml。要触发验证,请使用以下命令:

Test-XmlBySchema -XmlFile 'C:\Work\MorePeople.xml' -SchemaPath 'C:\Work\People.xsd' -Verbose

实际结果取决于MorePeople.xml 的内容。

让我们看两个例子。请注意,当 MorePeople.xml 没有错误时,上面的函数将返回 True。

[玩转系统] PowerShell 解析、读取和验证 XML 的魔力

MorePeople.xml 文件包含错误数据(Country 键拼写为 County)时,该函数将返回一些失败详细信息并返回 错误

[玩转系统] PowerShell 解析、读取和验证 XML 的魔力

正如您所看到的,详细输出中指定的错误信息非常丰富:它指向罪魁祸首文件并指向其中发生问题的确切组件。

微调验证模式

让我们看一下架构文件,然后看看如何使其变得更好。

默认情况下由 New-XmlSchema 创建的架构如下:

<?xml version="1.0" standalone="yes"?>
<xs:schema id="People" xmlns="" xmlns:xs="<http://www.w3.org/2001/XMLSchema>" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="People" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
    <xs:complexType>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="Person">
          <xs:complexType>
            <xs:attribute name="Name" type="xs:string" />
            <xs:attribute name="Country" type="xs:string" />
            <xs:attribute name="IsAdmin" type="xs:string" />
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:schema>

上面的默认模式很好,但并不完美。事实上,它发现了 Country 属性的拼写错误。但是,如果您按原样保留架构,以防未满足您可能的其他期望 - Test-XmlBySchema 验证不会将这些问题报告为错误。让我们解决这个问题。

下表列出了一些不被视为验证错误并且会被 Test-XmlBySchema 忽视的情况。在每一行上,右列显示如何手动更改架构以添加对必要保护的支持。

架构文件的修改版本(添加了所有保护)显示在表格后面。

将设置添加到默认架构 - 示例

Required Behavior

架构文件的相关设置

At least one Person element

Person 元素的 minOccurs 值设置为 1

Make Name, Country and IsAdmin attributes mandatory

use=”required” 添加到每个属性声明中

Allow only true/false values for IsAdmin

IsAdmin 声明设置 type=”xs:boolean”

Allow only Country names between 3 and 40 characters

使用xs:restriction (请参阅架构文本后的详细说明)

<?xml version="1.0" standalone="yes"?>
<xs:schema id="People" xmlns="" xmlns:xs="<http://www.w3.org/2001/XMLSchema>" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="People" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
    <xs:complexType>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="Person">
          <xs:complexType>
            <xs:attribute name="Name" type="xs:string" use="required" />
	    <xs:attribute name="Country" use="required">
	      <xs:simpleType>
		<xs:restriction base="xs:string">
		  <xs:minLength value="3"/>
		  <xs:maxLength value="40"/>
		</xs:restriction>
	      </xs:simpleType>
	    </xs:attribute>	
            <xs:attribute name="IsAdmin" type="xs:boolean" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:schema>

由于示例中的 IsAdmin 属性存在布尔值限制,其值必须为小写true 或 false。

使用 xs:restriction 验证字符串长度

字符串长度验证有点复杂。因此,尽管它在上面显示为修改后的架构的一部分,但它还是值得更多关注。

Country 属性的原始架构项(手动添加 use=”required” 后) 如下:

<xs:attribute name="Country" type="xs:string" use="required" />

要添加长度保护,您应该添加 元素,并在其中添加 。此限制又包含在 xs:minLengthxs:minLength 上声明的所需限制。

经过所有这些更改,最终的 xs:attribute 声明已从单行增长到巨大的 8 行节点:

<xs:attribute name="Country">
  <xs:simpleType>
	<xs:restriction base="xs:string">
	  <xs:minLength value="3"/>
	  <xs:maxLength value="40"/>
	</xs:restriction>
  </xs:simpleType>
</xs:attribute>

如果您在完成上述解释后没有感到头晕,那么您就有权查看正在执行的验证。为此,我们有意将值 Canada 缩短为两个字符的音节:Ca

设置好国家/地区短名称并保存 MorePeople.xml 后,您就可以运行下面的验证命令:

Test-XmlBySchema -XmlFile 'C:\Work\MorePeople.xml' -SchemaPath 'C:\Work\People.xsd' -Verbose

结果确实表明复杂的模式已经完成了它的工作:

[玩转系统] PowerShell 解析、读取和验证 XML 的魔力

XML 模式验证的复杂性可能会增加,并且可以验证您能想到的任何模式,尤其是与正则表达式结合使用时。

相关:PowerShell 和 Regex 入门

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯