繁体   English   中英

Powershell:读取/编辑命名节点

[英]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()方法。

    • 注意:请务必指定完整的 output 路径,因为 .NET 的工作目录通常与 PowerShell 的不同。

至于你尝试了什么:

$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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM