[英]Powershell: Read / Edit named nodes
我想从给定的 XML 文件中读取和编辑节点内容,并将更改后的内容写回。
给定的 XML 文件的内容:
<?xml version="1.0" encoding="UTF-8"?>
<SimBase.Document Type="ScenarioFile" version="4,5" id="Test">
<Descr>AceXML Document</Descr>
<Filename>Test.fxml</Filename>
<Flight.Sections>
...
<Section Name="DateTimeSeason">
<Property Name="Season" Value="Summer" />
<Property Name="Year" Value="2020" />
<Property Name="Day" Value="224" />
<Property Name="Hours" Value="12" />
<Property Name="Minutes" Value="2" />
<Property Name="Seconds" Value="17" />
</Section>
...
</Flight.Sections>
</SimBase.Document>
读取和写回 XML 文件的新副本工作正常,但我完全不知道如何读取/编辑名为“DateTimeSeason”的节点的内容。 它的节点,因为我不知道 XPath。
我写的脚本内容:
Write-Host "Edit XML file"
# Load local functions
. .\LocalFunctions.ps1
# Working environment
$flightFileDir = 'E:\Temp\PowerShell\P3D\'
$flightFileNameIn = 'MauleLSZH.fxml'
$flightFileNameOut = 'MauleLSZHNew.fxml'
$flightFileIn = $flightFileDir + $flightFileNameIn
$flightFileOut = $flightFileDir + $flightFileNameOut
# Get correct date and time for flight file
$dateTimeArr = SetupDateTime
#$dateTimeArr # output content of resulting array
# Set up new XML object and read file content
$xmlFlight = New-Object System.XML.XMLDocument
$xmlFlight.Load($flightFileIn)
# Edit content of node named 'DateTimeSeason'
$data = $xmlFlight.'SimBase.Document'.'Flight.Sections'.Section[@name -eq 'DateTimeSeason'].Property[@name = 'Year']
$data # output for test purposes
# Output modified flight file
$xmlFlight.Save($flightFileOut)
Write-Host "New XML file created"
非常感谢任何帮助或提示 Hannes
你很亲密; 这是一个更正后的版本,它添加了一个新的“属性”并编辑了现有属性的“值”。
# Set up new XML object and read file content
$xmlFlight = New-Object System.XML.XMLDocument
$xmlFlight.Load($flightFileIn)
# Create new "Property" and append to the "DateTimeSeason" node
[System.Xml.XmlElement]$newElement = $xmlFlight.CreateElement('Property')
$newElement.SetAttribute('Name','NewName')
$newElement.SetAttribute('Value','NewValue')
$data = $xmlFlight.'SimBase.Document'.'Flight.Sections'.SelectSingleNode("//Section[@Name='DateTimeSeason']")
[void]$data.AppendChild($newElement)
# Edit "Year" Property on the "DateTimeSeason" node
$xmlFlight.'SimBase.Document'.'Flight.Sections'.SelectSingleNode("//Section[@Name='DateTimeSeason']").SelectSingleNode("//Property[@Name = 'Year']").Value = '2021'
# Could also use: $data.SelectSingleNode("//Property[@Name = 'Year']").Value = '2021'
$data.Property # output for test purposes
# Output modified flight file
$xmlFlight.Save($flightFileOut)
Write-Host "New XML file created"
注意:XPath 区分大小写,“@name”在这里不起作用; 必须使用“@Name”。
要提供更简洁的 PowerShell 惯用解决方案,请使用Select-Xml
cmdlet:
# XPath query to locate the element of interest.
$xpathQuery = '/SimBase.Document/Flight.Sections/Section[@Name="DateTimeSeason"]/Property[@Name="Year"]'
# Get the element of interest (<Property Name="Year" Value="2020" />)...
$elem = (Select-Xml $xpathQuery $flightFileIn).Node
# ... update its 'Value' attribute ...
$elem.Value = 2021
# ... and save the modified document to the output file.
$elem.OwnerDocument.Save($flightFileOut)
使用Select-Xml
允许您使用 Xpath 查询直接从 XML 文件中提取信息,而无需首先通过[xml]
类型 ( System.Xml.XmlDocument
) 手动将文件内容解析为 XML DOM。
Select-Xml
返回Microsoft.PowerShell.Commands.SelectXmlInfo
实例,这些实例包装了 XPath 查询返回的 XML 节点 (System.Xml.XmlNode
),可以通过.Node
属性访问。
Select-Xml
直接返回 XML 节点会很方便,该节点在 PowerShell 7.1 中不存在,但它是GitHub 建议#13669的主题。 要访问封闭的 XML 文档 ( [xml]
) 实例,请使用节点的.OwnerDocument
属性,它允许您调用后者的.Save()
方法。
至于你尝试了什么:
$data = $xmlFlight.'SimBase.Document'.'Flight.Sections'.Section[@name -eq 'DateTimeSeason'].Property[@name = 'Year']
您错误地将 PowerShell 的基于属性的访问(例如.'SimBase.Document'.'Flight.Sections'
)与 XPath 语法(例如[@name -eq 'DateTimeSeason']
)混在一起。
虽然 PowerShell基于属性适配 XML DOM非常方便,但它没有内置查询功能,因此您需要使用 PowerShell 常用的过滤功能,特别是.Where()
数组方法(也可以使用Where-Object
cmdlet一个选项,但速度较慢):
# Returns element <Property Name="Year" Value="2020" />
$data = $xmlFlight.'SimBase.Document'.'Flight.Sections'.Section.
Where({ $_.Name -eq 'DateTimeSeason' }).Property.
Where({ $_.Name -eq 'Year' })
# Sample modification of the 'Value' attribute.
$data.Value = '2021'
或者,您可以混合基于属性的访问和 XPath 查询,但只能通过将 XPath 查询传递给对.SelectSingleNode()
或SelectNodes()
方法的调用,如Brian Reynolds 的回答所示。
注意:正如 Brian 指出的那样,XPath 和 XML 通常区分大小写- 与 PowerShell 基于属性的访问不同。
但是,为了概念清晰,我建议使用带有 PowerShell 过滤的基于属性的访问或文档(根)上的.SelectSingleNode()
/ SelectNodes()
方法:
$data = $xmlFlight.SelectSingleNode('/SimBase.Document/Flight.Sections/Section[@Name="DateTimeSeason"]/Property[@Name="Year"]')
或者,如果您愿意假设<Property>
元素仅作为<Section>
元素的子元素出现,例如,您可以通过//
使用快捷方式:
$data = $xmlFlight.SelectSingleNode("//Section/Property[@Name = 'Year']")
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.