适用于 PowerShell 5.1 及以上版本
虽然 JSON 已经成为现代应用配置的主流格式,但 XML 仍然在许多场景中扮演重要角色——Windows 配置文件(.config)、NuGet 包定义(.nuspec)、SOAP Web 服务、Office 文档(.docx/.xlsx 底层是 XML)、以及大量遗留系统的数据交换格式。PowerShell 通过 [xml] 类型加速器和 .NET 的 System.Xml 命名空间,提供了强大的 XML 处理能力。
本文将讲解 XML 文档的创建、查询(XPath)、修改,以及与常见 XML 格式的交互。
XML 解析与导航 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 [xml ]$config = @" <?xml version="1.0" encoding="UTF-8"?> <configuration> <appSettings> <add key="ServerName" value="prod-db01" /> <add key="Port" value="5432" /> <add key="Database" value="MyApp" /> <add key="Timeout" value="30" /> </appSettings> <connectionStrings> <add name="Default" connectionString="Server=prod-db01;Database=MyApp;Integrated Security=True" /> <add name="ReadOnly" connectionString="Server=prod-db01;Database=MyApp_ReadOnly;Integrated Security=True" /> </connectionStrings> <logging> <level>Warning</level> <path>C:\Logs\MyApp</path> <maxSizeMB>100</maxSizeMB> </logging> </configuration> "@ Write-Host "服务器:$ ($config .configuration.appSettings.add[0].value)" Write-Host "端口:$ ($config .configuration.appSettings.add[1].value)" Write-Host "日志级别:$ ($config .configuration.logging.level)" $settings = @ {}foreach ($add in $config .configuration.appSettings.add) { $settings [$add .key ] = $add .value } Write-Host "`nAppSettings:" $settings .GetEnumerator() | Sort-Object Key | ForEach-Object { Write-Host " $ ($_ .Key) = $ ($_ .Value)" } Write-Host "`n连接字符串:" foreach ($cs in $config .configuration.connectionStrings.add) { Write-Host " $ ($cs .name):$ ($cs .connectionString)" }
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 服务器:prod-db01 端口:5432 日志级别:Warning AppSettings: Database = MyApp Port = 5432 ServerName = prod-db01 Timeout = 30 连接字符串: Default :Server =prod-db01;Database =MyApp;Integrated Security =True ReadOnly:Server =prod-db01;Database =MyApp_ReadOnly;Integrated Security =True
XPath 查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 [xml ]$servers = @" <inventory> <server env="production" role="web"> <name>WEB01</name> <ip>10.0.1.10</ip> <cpu>8</cpu> <ram>32</ram> <status>running</status> </server> <server env="production" role="db"> <name>DB01</name> <ip>10.0.1.20</ip> <cpu>16</cpu> <ram>64</ram> <status>running</status> </server> <server env="staging" role="web"> <name>WEB-STG01</name> <ip>10.0.2.10</ip> <cpu>4</cpu> <ram>16</ram> <status>stopped</status> </server> <server env="production" role="web"> <name>WEB02</name> <ip>10.0.1.11</ip> <cpu>8</cpu> <ram>32</ram> <status>running</status> </server> </inventory> "@ $nav = $servers .CreateNavigator()$prodServers = $servers .SelectNodes("//server[@env='production']" )Write-Host "生产环境服务器:" -ForegroundColor Cyanforeach ($s in $prodServers ) { Write-Host " $ ($s .name) ($ ($s .ip)) - $ ($s .status)" }$webServers = $servers .SelectNodes("//server[@role='web']" )Write-Host "`nWeb 服务器:" -ForegroundColor Cyanforeach ($s in $webServers ) { Write-Host " $ ($s .name) - CPU: $ ($s .cpu), RAM: $ ($s .ram)GB" }$powerful = $servers .SelectNodes("//server[@env='production' and cpu > 8]" )Write-Host "`n高配生产服务器(CPU>8):" -ForegroundColor Cyanforeach ($s in $powerful ) { Write-Host " $ ($s .name) - CPU: $ ($s .cpu)" }$totalCpu = $servers .SelectNodes("sum(//server[@env='production']/cpu)" )$totalRam = $servers .SelectNodes("sum(//server[@env='production']/ram)" )Write-Host "`n生产环境总资源:CPU $totalCpu 核,RAM $totalRam GB"
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 生产环境服务器: WEB01 (10.0 .1.10 ) - running DB01 (10.0 .1.20 ) - running WEB02 (10.0 .1.11 ) - running Web 服务器: WEB01 - CPU: 8 , RAM: 32 GB WEB-STG01 - CPU: 4 , RAM: 16 GB WEB02 - CPU: 8 , RAM: 32 GB 高配生产服务器(CPU> 8 ): DB01 - CPU: 16 生产环境总资源:CPU 32 核,RAM 128 GB
XML 创建与修改 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 function New-ReportXml { param ( [string ]$ReportName = "系统巡检报告" , [string ]$OutputPath = "C:\Reports\report.xml" ) $xmlDoc = [System.Xml.XmlDocument ]::new() $declaration = $xmlDoc .CreateXmlDeclaration("1.0" , "UTF-8" , $null ) $xmlDoc .AppendChild($declaration ) | Out-Null $root = $xmlDoc .CreateElement("report" ) $root .SetAttribute("name" , $ReportName ) $root .SetAttribute("generated" , (Get-Date -Format "yyyy-MM-dd HH:mm:ss" )) $serversNode = $xmlDoc .CreateElement("servers" ) foreach ($srvName in @ ("WEB01" , "WEB02" , "DB01" )) { $srvNode = $xmlDoc .CreateElement("server" ) $srvNode .SetAttribute("name" , $srvName ) $srvNode .SetAttribute("status" , "running" ) $cpuNode = $xmlDoc .CreateElement("cpu" ) $cpuNode .InnerText = Get-Random -Min 10 -Max 90 $srvNode .AppendChild($cpuNode ) | Out-Null $memNode = $xmlDoc .CreateElement("memory" ) $memNode .InnerText = Get-Random -Min 20 -Max 80 $srvNode .AppendChild($memNode ) | Out-Null $serversNode .AppendChild($srvNode ) | Out-Null } $root .AppendChild($serversNode ) | Out-Null $xmlDoc .AppendChild($root ) | Out-Null $xmlDoc .Save($OutputPath ) Write-Host "XML 报告已生成:$OutputPath " -ForegroundColor Green } New-ReportXml [xml ]$doc = Get-Content "C:\Reports\report.xml" $server = $doc .SelectSingleNode("//server[@name='WEB01']" )if ($server ) { $server .status = "maintenance" Write-Host "已修改 WEB01 状态为 maintenance" -ForegroundColor Yellow } $newServer = $doc .CreateElement("server" )$newServer .SetAttribute("name" , "WEB03" )$newServer .SetAttribute("status" , "running" )$cpu = $doc .CreateElement("cpu" )$cpu .InnerText = "25" $newServer .AppendChild($cpu ) | Out-Null $doc .SelectSingleNode("//servers" ).AppendChild($newServer ) | Out-Null Write-Host "已添加新服务器 WEB03" -ForegroundColor Green$doc .Save("C:\Reports\report.xml" )
执行结果示例:
1 2 3 XML 报告已生成:C :\Reports\report.xml 已修改 WEB01 状态为 maintenance已添加新服务器 WEB03
XML 与对象转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 $servers = @ ( [PSCustomObject ]@ { Name = "SRV01" ; IP = "10.0.1.1" ; Role = "Web" }, [PSCustomObject ]@ { Name = "SRV02" ; IP = "10.0.1.2" ; Role = "DB" }, [PSCustomObject ]@ { Name = "SRV03" ; IP = "10.0.1.3" ; Role = "App" } ) $servers | Export-Clixml -Path "C:\Data\servers.xml" -Encoding UTF8Write-Host "对象已导出为 Clixml" -ForegroundColor Green$imported = Import-Clixml -Path "C:\Data\servers.xml" $imported | Format-Table -AutoSize $config = [PSCustomObject ]@ { AppName = "MyApp" Version = [version ]"2.5.0" StartTime = Get-Date Servers = $servers } $config | Export-Clixml -Path "C:\Data\config.xml" $restored = Import-Clixml -Path "C:\Data\config.xml" Write-Host "应用:$ ($restored .AppName) v$ ($restored .Version)" Write-Host "类型保留:Version=$ ($restored .Version.GetType().Name)"
执行结果示例:
1 2 3 4 5 6 7 8 对象已导出为 Clixml Name IP Role SRV01 10.0 .1 .1 Web SRV02 10.0 .1 .2 DB SRV03 10.0 .1 .3 App 应用:MyApp v2.5 .0 类型保留:Version=Version
注意事项
XPath 区分大小写 :XML 元素和属性名是大小写敏感的,查询时注意匹配
命名空间 :带命名空间的 XML(如 SOAP)需要使用 XmlNamespaceManager 才能用 XPath 查询
编码 :XML 文件可能有各种编码,使用 Get-Content -Encoding UTF8 或 XmlDocument.Load() 正确处理
大文件 :超大 XML 文件(>100MB)应使用 XmlReader(前向只读)代替 XmlDocument(全加载到内存)
Clixml 安全 :Export-Clixml 可以安全存储 PSCredential 对象(DPAPI 加密),但仅限同一台机器解密
修改后保存 :通过点表示法修改 XML 属性后,需要调用 Save() 方法持久化到文件