使用PowerShell如何覆盖XML节点中的现有数据



我有一些XML,希望能够覆盖STATUS标记。

外观:

<START>
<EXAMPLE>
<THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
</EXAMPLE>
</START>

我想要它的样子:

<START>
<EXAMPLE>
<THINGS>
<STUFF>
<STATUS>Open</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<STUFF>
<STATUS>NA</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<STUFF>
<STATUS>Closed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<STUFF>
<STATUS>Open</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<STUFF>
<STATUS>Open</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<STUFF>
<STATUS>NA</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
</EXAMPLE>
</START>

有了这段代码,我可以将每个STATUS标记更改为正确的值,当我在脚本运行后仅运行$XMLStatus后检查控制台时,它会显示出它应该显示的所有内容,但当我在记事本中打开XML时就不会了。它仍然将所有内容显示为Not_Reviewed。我不知道我在这里做错了什么,我们非常感谢你的帮助。

[xml]$xmldoc = Get-Content -Path 'C:test.xml'
$XMLStatus = $xmldoc.START.EXAMPLE.THINGS.STUFF.STATUS
$XMLcount = 0
foreach($one in $XMLStatus[$XMLcount]){           
foreach($status in $one){
$one = $myvalue
Switch ($one){
1 {$status = "Open"; break;}
2 {$status = "Closed"; break;}
3 {$status = "NA"; break;}
default {"Open"}
}
$XMLStatus[$XMLcount] = $status
}
}

$xmldoc.Save('C:test.xml')
$XMLcount++

不需要通过索引访问单个元素。使用XPath查询将所有STATUS元素选择到XPathNodeList中。使用foreach循环对其进行迭代,并使用switch语句更改InnerText属性。像这样,

[xml]$xmldoc = get-content c:whatever.xml
# Select all STATUS elements. NB: element names in XPath are case-sensitive
$nl = $xmldoc.selectnodes('/START/EXAMPLE/THINGS/STUFF/STATUS')
# Loop through the nodes and change InnerText numbers to string values
foreach($n in $nl) {
switch($n.InnerText){
1 {$s = "Open"; break;}
2 {$s = "Closed"; break;}
3 {$s = "NA"; break;}
default {$s = "Open"}
}
$n.InnerText = $s
}
# Print the results on console
$xmldoc.Save([console]::out)

在PowerShell中并不经常使用xml,但我想我知道发生了什么。

您的xml被复制粘贴得很有趣,并使其无效。你丢失了一些标签。

正因为如此,我采取了一些自由,做出了自己的决定。

<START>
<EXAMPLE>
<THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
</EXAMPLE>
</START>

首先,您没有在文件中看到值的更改,是因为您没有将这些更改保存到文件中。您将$xmldoc.START.EXAMPLE.THINGS.STUFF.STATUS的值放入一个变量中,然后使用该变量,只对该变量进行更改,而不是对原始文档变量进行更改。

其次,在您的示例中,$myvalue没有设置在任何位置,所以在您的交换机中,所有内容都将默认为您的默认情况。

第三,默认情况下除了打印";打开";到stdout。

最后,您需要避免试图通过获取对象的值并更改该值来更改值。我知道这听起来很令人困惑,所以让我们来举一个例子。

我们认为我们想要修改的是$xmldoc.START.EXAMPLE.THINGS.STUFF.STATUS[i]的值,但这就是我们错的地方。我们不想设置.STATUS[i]的值,因为它只是一个字符串对象,当您运行它并查看TypeName时,您可以看到它。

$xmldoc.START.EXAMPLE.THINGS.STUFF.STATUS[0] | Get-Member

实际上,我们想要修改的是STATUS属性,它位于STUFF元素中。所以如果你做这个

$xmldoc.START.EXAMPLE.THINGS.STUFF[0] | Get-Member

您会注意到,TypeName是System.Xml.XmlElement,那里有一个名为STATUS的属性,在definition列中,我们看到它是一个字符串,我们知道我们可以获得这个值,并将其设置为{get;set;}以供参考,这些被称为getter和setter。

为了修改该值,我们需要设置STATUS属性,如下所示``$xmldoc.START.EXAMPLE.THINGS.STUFF[0].STATUS=";不管什么"`

所以经过一些修改,这就是我的想法。评论是我做出改变的地方。

[xml]$xmldoc = "
<START>
<EXAMPLE>
<THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
</EXAMPLE>
</START>"
#set myyvalue
$myvalue = 1
# removed this var completely
#$XMLStatus = $xmldoc.START.EXAMPLE.THINGS.STUFF.STATUS
$XMLcount = 0
# for the foreach, we want to work with each object in the STUFF array
# I also removed the extra foreach, as it seemed unnecessary
foreach($one in $xmldoc.START.EXAMPLE.THINGS.STUFF[$XMLcount].STATUS)
{
# made the switch, switch off of $myvalue           
Switch ($myvalue){
1 {$status = "Open"; break;}
2 {$status = "Closed"; break;}
3 {$status = "NA"; break;}
# fixed the default case to properly set the var
default {$status = "Open"}
}
$xmldoc.START.EXAMPLE.THINGS.STUFF[$XMLcount].STATUS = $status
}        

$xmldoc.Save('C:temptest.xml')
$XMLcount++

如果你想要一个通过运行一次遍历所有内容的版本,你可以得到STUFF的长度,并使用while循环

[xml]$xmldoc = "
<START>
<EXAMPLE>
<THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
<THINGS>
<STUFF>
<STATUS>Not_Reviewed</STATUS>
<DETAILS>1111 2222 3333 4444 5555 6666 7777 8888</DETAILS>
</STUFF>
</THINGS>
</EXAMPLE>
</START>"

$myvalue = 1
$numberOfElements = $xmldoc.START.EXAMPLE.THINGS.Length
$XMLcount = 0
while($XMLcount -lt $numberOfElements)
{
foreach($one in $xmldoc.START.EXAMPLE.THINGS.STUFF[$XMLcount].STATUS)
{           
Switch ($myvalue){
1 {$status = "Open"; break;}
2 {$status = "Closed"; break;}
3 {$status = "NA"; break;}
default {$status = "Open"}
}
$xmldoc.START.EXAMPLE.THINGS.STUFF[$XMLcount].STATUS = $status
}
$XMLcount++        
} 
$xmldoc.Save('C:temptest.xml')

最新更新