在 Powershell 中将 x XML 节点设置为值
Set x XML node to value in Powershell
我有这个 PowerShell 脚本,其中 XML 文件的值将由脚本设置。当所有子节点都被唯一命名时,这工作得很好。但是,我正在将 XML 文件调整为重复某些节点的文件。现在我在 Powershell 中遇到错误。
我的问题是,如何通过 PowerShell 将 XML 中的第 X 个节点设置为某个值?
简而言之,我的脚本如下所示:
cls
[xml] $xml1 = '<Lvl1>
<Lvl2>""</Lvl2>
<Lvl2>""</Lvl2>
</Lvl1>'
$xml1.Lvl1.Lvl2='./'
$xml1.Save("text.xml")
有两次相同的节点 (Lvl2),这就是我在 PowerShell 中收到以下错误的原因:"Cannot set "Lvl2" 因为只能将字符串用作设置 XmlNode 属性的值。"
当我删除一个 (Lvl2) 节点时,该脚本非常有效。
请指教。
您始终可以使用 .NET 语法,它很有魅力。
$xml1.SelectSingleNode('Lvl1/Lvl2[2]').InnerText='./'
$xml1 = [xml]@'
<Lvl1>
<Lvl2>""</Lvl2>
<Lvl2>""</Lvl2>
</Lvl1>
'@
$xml1.GetElementsByTagName('Lvl2') | ForEach-Object { $_.InnerText = './' }
$xml1.Save("text.xml")
Get-Content -Path "text.xml" # debugging output
调试输出:.\SO280990.ps1
<Lvl1>
<Lvl2>./</Lvl2>
<Lvl2>./</Lvl2>
</Lvl1>
上述方法甚至适用于更复杂的输入数据,例如如下:
$xml1 = [xml]@'
<root>
<Lvl1>
<Lvl2>"a"</Lvl2>
<Lvl2>"b"</Lvl2>
</Lvl1>
<Lvl0>
<Lvl1>
<Lvl2>"c"</Lvl2>
<Lvl2>"d"</Lvl2>
</Lvl1>
</Lvl0>
</root>
'@
可以将Lvl1
的子节点作为数组获取,然后使用索引调整想要的节点:
[xml] $xml1 = '<Lvl1>
<Lvl2>""</Lvl2>
<Lvl2>""</Lvl2>
</Lvl1>'
$lvl2Nodes = @($xml1.Lvl1.ChildNodes)
$lvl2Nodes[1].'#text' = "blah" # updating the second childnode only
$xml1.Save("D:\text.xml")
结果:
<Lvl1>
<Lvl2>""</Lvl2>
<Lvl2>blah</Lvl2>
</Lvl1>
PS D:\TEMP> type lvl.xml
<Lvl1>
<Lvl2>"test1"</Lvl2>
<Lvl2>"test2"</Lvl2>
</Lvl1>
PS D:\TEMP> $xml = New-Object XML
PS D:\TEMP> $xml.Load("lvl.xml")
PS D:\TEMP> $element = $xml.SelectSingleNode("/Lvl1/Lvl2[2]")
PS D:\TEMP> $element.InnerText ="bla"
PS D:\TEMP> $xml.Save("lvl2.xml")
PS D:\TEMP> type lvl2.xml
<Lvl1>
<Lvl2>"test1"</Lvl2>
<Lvl2>bla</Lvl2>
</Lvl1>
PS D:\TEMP>
在 XmlPath(即“/Lvl1/Lvl2[2]”)中,“[2]”指的是该对象第二次存在。
provides a concise solution that bypasses ,它使用熟悉的 点符号 ,有利于直接使用 .NET 方法。
但是,PowerShell 的点表示法既方便又使用熟悉的语法,因此值得在可能和适当的情况下坚持使用。
不幸的是,在手头的情况下,这只能通过 混合 方法,结合点符号(使用元素或属性名称与 属性 名称一样直接)具有基础 System.Xml.XmlNode
类型的本机属性:
注意:我在下面的命令中使用了以下修改后的 XML 和变量名称 $xml
:
[xml] $xml = @'
<Lvl1>
<Lvl2>a</Lvl2>
<Lvl2>b</Lvl2>
<Other><Stuff>c</Stuff></Other>
</Lvl1>
'@
给firstLvl2
child赋值,可以使用以下方法:
# Assign to the *first* Lvl2 element.
$xml.Lvl1['Lvl2'].InnerText = './'
使用 索引器 (['Lvl2']
) 而不是 点符号 (.Lvl2
) 利用 System.Xml.XmlElement
类型的 type-native indexer 通过给定的 name[= 定位 first 子元素100=].
那个元素的.InnerText
属性然后可以赋值给。
如果您需要分配给具有给定索引的子元素,则需要一种不同的方法,使用数字 (0
-based) XmlElement
的 .ChildNodes
集合的索引( 的简化版本):
# Assign to the *last* Lvl2 element.
$xml1.Lvl1.ChildNodes[-1].InnerText = 'last'
从 PowerShell [Core] 7.0 开始, 不起作用:
不幸的是,单独使用点符号直接索引到子元素中 不 assignments[=101] =]:
# DOESN'T WORK: PowerShell *quietly ignores* the assignment.
$xml.Lvl1.Lvl2[-1] = 'last'
这种令人惊讶的行为已在 this GitHub issue 中报告。
但是,通过这种方式(通常)获取一个值效果很好:
# OK
PS $xml.Lvl1.Lvl2[0]
a
警告:
如果命名元素恰好是唯一子元素和它本身有元素children,索引 不 工作 ,阻止 PowerShell 通常提供的标量和集合的统一处理:
# DOES NOT WORK, because there is only a single 'Other' element
# *and* it has a child element.
PS $xml.Lvl1.Other[0]
# !! No output
令人惊讶的是,PowerShell 使用原生类型 by name 索引器来查找元素 named '0'
-定义失败,因为 XML 元素名称不能以 digits.
开头
This GitHub suggestion 建议在这种情况下使用 PowerShell 的自动位置索引,基于参数是 integer.
我有这个 PowerShell 脚本,其中 XML 文件的值将由脚本设置。当所有子节点都被唯一命名时,这工作得很好。但是,我正在将 XML 文件调整为重复某些节点的文件。现在我在 Powershell 中遇到错误。
我的问题是,如何通过 PowerShell 将 XML 中的第 X 个节点设置为某个值?
简而言之,我的脚本如下所示:
cls
[xml] $xml1 = '<Lvl1>
<Lvl2>""</Lvl2>
<Lvl2>""</Lvl2>
</Lvl1>'
$xml1.Lvl1.Lvl2='./'
$xml1.Save("text.xml")
有两次相同的节点 (Lvl2),这就是我在 PowerShell 中收到以下错误的原因:"Cannot set "Lvl2" 因为只能将字符串用作设置 XmlNode 属性的值。"
当我删除一个 (Lvl2) 节点时,该脚本非常有效。
请指教。
您始终可以使用 .NET 语法,它很有魅力。
$xml1.SelectSingleNode('Lvl1/Lvl2[2]').InnerText='./'
$xml1 = [xml]@'
<Lvl1>
<Lvl2>""</Lvl2>
<Lvl2>""</Lvl2>
</Lvl1>
'@
$xml1.GetElementsByTagName('Lvl2') | ForEach-Object { $_.InnerText = './' }
$xml1.Save("text.xml")
Get-Content -Path "text.xml" # debugging output
调试输出:.\SO280990.ps1
<Lvl1> <Lvl2>./</Lvl2> <Lvl2>./</Lvl2> </Lvl1>
上述方法甚至适用于更复杂的输入数据,例如如下:
$xml1 = [xml]@'
<root>
<Lvl1>
<Lvl2>"a"</Lvl2>
<Lvl2>"b"</Lvl2>
</Lvl1>
<Lvl0>
<Lvl1>
<Lvl2>"c"</Lvl2>
<Lvl2>"d"</Lvl2>
</Lvl1>
</Lvl0>
</root>
'@
可以将Lvl1
的子节点作为数组获取,然后使用索引调整想要的节点:
[xml] $xml1 = '<Lvl1>
<Lvl2>""</Lvl2>
<Lvl2>""</Lvl2>
</Lvl1>'
$lvl2Nodes = @($xml1.Lvl1.ChildNodes)
$lvl2Nodes[1].'#text' = "blah" # updating the second childnode only
$xml1.Save("D:\text.xml")
结果:
<Lvl1> <Lvl2>""</Lvl2> <Lvl2>blah</Lvl2> </Lvl1>
PS D:\TEMP> type lvl.xml
<Lvl1>
<Lvl2>"test1"</Lvl2>
<Lvl2>"test2"</Lvl2>
</Lvl1>
PS D:\TEMP> $xml = New-Object XML
PS D:\TEMP> $xml.Load("lvl.xml")
PS D:\TEMP> $element = $xml.SelectSingleNode("/Lvl1/Lvl2[2]")
PS D:\TEMP> $element.InnerText ="bla"
PS D:\TEMP> $xml.Save("lvl2.xml")
PS D:\TEMP> type lvl2.xml
<Lvl1>
<Lvl2>"test1"</Lvl2>
<Lvl2>bla</Lvl2>
</Lvl1>
PS D:\TEMP>
在 XmlPath(即“/Lvl1/Lvl2[2]”)中,“[2]”指的是该对象第二次存在。
但是,PowerShell 的点表示法既方便又使用熟悉的语法,因此值得在可能和适当的情况下坚持使用。
不幸的是,在手头的情况下,这只能通过 混合 方法,结合点符号(使用元素或属性名称与 属性 名称一样直接)具有基础 System.Xml.XmlNode
类型的本机属性:
注意:我在下面的命令中使用了以下修改后的 XML 和变量名称 $xml
:
[xml] $xml = @'
<Lvl1>
<Lvl2>a</Lvl2>
<Lvl2>b</Lvl2>
<Other><Stuff>c</Stuff></Other>
</Lvl1>
'@
给firstLvl2
child赋值,可以使用以下方法:
# Assign to the *first* Lvl2 element.
$xml.Lvl1['Lvl2'].InnerText = './'
使用 索引器 (
['Lvl2']
) 而不是 点符号 (.Lvl2
) 利用System.Xml.XmlElement
类型的 type-native indexer 通过给定的 name[= 定位 first 子元素100=].那个元素的
.InnerText
属性然后可以赋值给。
如果您需要分配给具有给定索引的子元素,则需要一种不同的方法,使用数字 (0
-based) XmlElement
的 .ChildNodes
集合的索引(
# Assign to the *last* Lvl2 element.
$xml1.Lvl1.ChildNodes[-1].InnerText = 'last'
从 PowerShell [Core] 7.0 开始, 不起作用:
不幸的是,单独使用点符号直接索引到子元素中 不 assignments[=101] =]:
# DOESN'T WORK: PowerShell *quietly ignores* the assignment.
$xml.Lvl1.Lvl2[-1] = 'last'
这种令人惊讶的行为已在 this GitHub issue 中报告。
但是,通过这种方式(通常)获取一个值效果很好:
# OK
PS $xml.Lvl1.Lvl2[0]
a
警告:
如果命名元素恰好是唯一子元素和它本身有元素children,索引 不 工作 ,阻止 PowerShell 通常提供的标量和集合的统一处理:
# DOES NOT WORK, because there is only a single 'Other' element
# *and* it has a child element.
PS $xml.Lvl1.Other[0]
# !! No output
令人惊讶的是,PowerShell 使用原生类型 by name 索引器来查找元素 named '0'
-定义失败,因为 XML 元素名称不能以 digits.
This GitHub suggestion 建议在这种情况下使用 PowerShell 的自动位置索引,基于参数是 integer.