Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Question
Wednesday, January 13, 2010 7:46 PM
Hi! I have xml file with data like:
...
<Item>
<value>aaa</value>
<value>bbb</value>
</Item>
...
I need change "aaa" and "bbb" to "111" and "222" and add value "333".
In result a want see data like:
...
<Item>
<value>111</value>
<value>222</value>
<value>333</value>
</Item>
...
How i can do it with powershell?
All replies (9)
Thursday, January 14, 2010 3:01 AM ✅Answered | 2 votes
I knew I'd get it eventually.... Using get-member wasn't giving me everything available. I happened to use getenumerator() on the element, and it gave me a whole bunch of other properties.... one of which was innertext. Anyway, this is a complete script that does everything you requested. Now don't ask me about xml attributes... I've had it with this stuff for another two weeks!
[xml] $xml = gc .\file.xml
$newitem = $xml.CreateElement("Value")
$newitem.set_InnerXML("333")
# Write nodes in XML:
$xml.items.AppendChild($newitem)
$nodes = @()
$array = New-Object System.Collections.Arraylist
foreach ($node in $xml.Items.ChildNodes){
$array.add($node )
}
$array |foreach {
if ($_.innertext -eq "aaa") {
$newitem = $xml.CreateElement("Value")
$newitem.set_InnerXML("111")
$xml.Items.ReplaceChild($newitem, $_)
}
if ($_.innertext -eq "bbb") {
$newitem = $xml.CreateElement("Value")
$newitem.set_InnerXML("222")
$xml.Items.ReplaceChild($newitem, $_)
}
}
$xml.save("C:\Users\Tome\scripts\new.xml")
Thursday, January 14, 2010 3:03 AM ✅Answered | 1 vote
Sorry... forgot to mention one other thing. The reason <Item> doesn't work is because the xmldocument has a property called Item. It also adds all tags as properties. Therefore it can't add the xml node Item because there is already a property with that name.
Wednesday, January 13, 2010 8:43 PM
This is one way:
$xml1 = @()
$xml0 = gc test.xml
$xml0 |% {
switch ($_) {
"<value>aaa<\value>" {$xml1 += "<value>111<\value>"}
"<value>bbb<\value>" {$xml1 += "<value>222<\value>";$xml1 += "<value>333<\value>"}
default {$xml1 += $_}
}
}
$xml1
Wednesday, January 13, 2010 9:51 PM
I was looking at the xml implementation in Powershell a few weeks ago. Based on my notes I can tell you how to add the new element, but I'm not sure how to change the existing ones yet.
One thing to note... I ran this with your xml, and had problems because you are using the tag <Item> for some reason. Powershell does not seem to like that. I changed Item to Items and used the following:
[xml] $xml = gc .\file.xml
$newitem = $xml.CreateElement("Value")
$newitem.set_InnerXML("333")
# Write nodes in XML:
$xml.items.AppendChild($newitem)
You can then see that $xml.items has the new value by running:
$xml.items
....but I also ran into another snag while trying to export to file. For some reason xmldocument.save does not work properly for me, and neither does $xml|write-file new.xml
Just for reference this uses the .net implementation of xmldocument/xmlelement/xmlnode
I'm going to try and revisit this some point soon, but hopefully someone else can help us out before then.
Thursday, January 14, 2010 2:39 AM
I got really far on this.... Only one hurdle left. I can't seem to test for $_ -eq "aaa" or -match "aaa". If anyone can help me get that piece it would be great.... If you remove that test it does successfully write over all elements. I had to use the arraylist because of the way powershell was handling the foreach $node loop. For some reason processing in the loop caused the childnodes to change. It would only process the first element and then stop. I tried using a powershell array, but I couldn't add the objects to the array with += so I just went with an arraylist to make it work.
Also to note.... the reason xmldocument.save wasn't working for me was b/c I wasn't putting in the full path:
[xml] $xml = gc .\file.xml
$newitem = $xml.CreateElement("Value")
$newitem.set_InnerXML("333")
# Write nodes in XML:
$xml.items.AppendChild($newitem)
$nodes = @()
$array = New-Object System.Collections.Arraylist
foreach ($node in $xml.Items.ChildNodes){
$array.add($node )
}
$array |foreach {
if ($_ -eq "aaa") {
$newitem = $xml.CreateElement("Value")
$newitem.set_InnerXML("111")
$xml.Items.ReplaceChild($newitem, $_)
}
}
$xml.save("C:\Users\Tome\scripts\new.xml")
Thursday, January 14, 2010 2:44 AM
fyi... the biggest problem I've had is that there is a property called #text that seems to be used in these elements. That's where the values can be get/set. My above method to use ReplaceChild is my way of getting around this when replacing, but I'm pretty sure this is why I can't get the value I want to use -match or -eq against.
Thursday, January 14, 2010 4:53 PM
Sorry... forgot to mention one other thing. The reason <Item> doesn't work is because the xmldocument has a property called Item. It also adds all tags as properties. Therefore it can't add the xml node Item because there is already a property with that name.
Thank you! Item is only sample, in real i have another name and you code work. But, now, i have another problem. How i can remove old all "Genre"s and replace with mew from array?
<Genres>
<Genre>111</Genre>
<Genre>222</Genre>
<Genre>333</Genre>
</Genres>
$xml = [xml](get-content file)
$root = $xml.get_DocumentElement();
$arr = @("asd","dfg","ert","456")
for ( $i = 0; $i -lt $arr.count; $i+=1 ){
if ($root.Item.Value[$i] -ne $null){
#how i can replace element?
$newitem = $xml.CreateElement("Genre")
$newitem.set_InnerXML($arr[$i])
$xml.Item.ReplaceChild($newitem, $_)
}
else{
$newitem = $xml.CreateElement("Genre")
$newitem.set_InnerXML($arr[$i])
$root.Item.AppendChild($newitem)
}
}
Friday, January 15, 2010 3:41 PM
The $xml variable is populated with a properties for each sub element.... Therefore you need to use $xml.genres instead of $xml.items. There's also a removeall() function that will do what you want to do:
$xml = [xml](get-content file.xml)
$arr = @("asd","dfg","ert","456")
$genres = $xml.Genres
$xml.Genres.RemoveAll()
for ( $i = 0; $i -lt $arr.count; $i+=1 ){
$newitem = $xml.CreateElement("Genre")
$newitem.set_InnerXML($arr[$i])
$genres.AppendChild($newitem)
}
$xml.save('C:\Users\tome\scripts\new.xml')
Thursday, May 9, 2013 4:25 PM
# You can't and shouldn't use the root name Item since it is protected XML property in Powershell.
# Solution: Change Item to for example Items
$file = "C:\temp\Items.xml"
[xml] $xml = Get-Content -Path $file
$xml.PreserveWhitespace = $true
$xpath = "//value"
ForEach($val in $xml.SelectNodes($xpath)){
switch ($val.InnerXML){
"aaa" {
$val.InnerXML = "111"
break;
}
"bbb" {
$val.InnerXML = "222"
break;
}
default {}
}
}
$inner=$xml.CreateElement("value");
$inner.InnerXML="333";
$xml.items.AppendChild($inner)
# Note that the $file must be the absolute (including path) name
$xml.Save($file)