简体   繁体   中英

How to Edit and Save XML nodes with PowerShell

I cannot seem to get this to work. I want to use PowerShell to select all nodes of a particular type, edit them, then save back to disk.

Here is the starting XML file:

<Cars>
  <Car>Car1</Car>
  <Car>Car2</Car>
</Cars>

Here is the file after being modified:

<Cars>
  <Car Text="Car1"></Car>
  <Car Text="Car2"></Car>
</Cars>

Have tried:

[xml]$xaml = Get-Content -Path "C:\Test\TranslationUtility\cars.xml" |
             Select-Xml -XPath "//Car" |
             Write-Host $_.InnerText;

You need to iterate through the results in the pipeline. You have more than one result from searching for cars. At the end, you will want to have % {$_.Node.Text} instead of Write-Host $_.InnerText;

The % is a shortcut for For-Each Object . So it basically loops through each entry and displays the information you need.

Here is how I'd approach this.

First, you get the content from the xml file, then you use SelectNodes to find the nodes you want, you iterate them, create the attribute and assign a value.

Once this is done, all that remains is to save the xml back.

[xml]$MyXML = Get-Content 'C:\__tmp\YourXmlFile.xml'

$Cars  = $MyXML.SelectNodes('//Car')

$Cars | foreach {
$TextAttrib = $_.OwnerDocument.CreateAttribute('text') 
$_.Attributes.Append($TextAttrib) |Out-Null;
$_.SetAttribute('text','My Car text...')
}

$MyXML.Save('C:\__tmp\YourXmlFile.xml')

As you can see, mine only write a static text, My Car text... but you can tailor this to suit your needs.

The code you posted should throw a bunch of errors, because Get-Content (without the parameter -Raw ) produces an array of strings, each of which is invalid XML by itself. Feeding that into Select-Xml doesn't work. Also, your use of the [xml] type accelerator and Write-Host is wrong.

Rule of thumb:

  • If you want to use Select-Xml let it read the file by itself (via its -Path parameter):

     $xpath = '//Car' $xmlfile = 'C:\\Test\\TranslationUtility\\cars.xml' Select-Xml -Xpath $xpath -Path $xmlfile 
  • If you want to use Get-Content and the [xml] type accelerator use the SelectNodes() method:

     $xpath = '//Car' $xmlfile = 'C:\\Test\\TranslationUtility\\cars.xml' [xml]$xml = Get-Content $xmlfile $xml.SelectNodes($xpath) 

However, that alone won't allow you to achieve your desired result, because you want to manipulate XAML files. Please don't omit such crucial information from your questions. I'm only aware because I was just about to respond to your previous question when you deleted it.

XAML files are always using namespaces, so you must use a namespace manager to take care of that, like this:

$xpath   = '//Car'
$xmlfile = 'C:\Test\TranslationUtility\cars.xml'


Select-Xml -Xpath $xpath -Path $xmlfile 

or like this:

$xpath   = '//Car'
$xmlfile = 'C:\Test\TranslationUtility\cars.xml'

[xml]$xml = Get-Content $xmlfile



$xml.SelectNodes($xpath, )

Since you want to modify the XML data I'd probably go with the latter approach. That allows you to add an attribute like this:

$i = 1
$xml.SelectNodes($xpath, $nsm) | ForEach-Object {
    [void]$_.SetAttribute('Text', "Car$i")
    $i++
}

Save the modified XML via the Save() method:

$xml.Save('C:\path\to\output.xml')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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