views:

516

answers:

1

How can I get a list of multiple XML files from a specified directory and for each file add an element under the second root node using powershell?

Example: I want to add <LastName>SomeName</LastName> within the FIRST <Names> element:

<People>
  <Names>
      <FirstName>someFirstName</FirstName>
  </Names>
  <Names>
      <FirstName>myFirstName</FirstName>
      <Address>SomeAddress</Address>
  </Names>
</People>

Will become:

<People>
  <Names>
      <LastName>SomeName</LastName>
      <FirstName>someFirstName</FirstName>
  </Names>
  <Names>
      <FirstName>myFirstName</FirstName>
      <Address>SomeAddress</Address>
  </Names>
</People>
+3  A: 

You can do it using CreateElement and AppendChild method

Get-ChildItem c:\temp\ *.xml | 
    % { 
        $xml      = [xml](Get-Content $_.fullname)
        $lastName = $xml.CreateElement('LastName')
        $lastName.PsBase.InnerText = 'SomeName'
        $null     = $xml.People.Names[0].AppendChild($lastName)
        $xml.Save($_.FullName)
    }

In case that you run PowerShell V2, you don't need to use property PsBase:

        $lastName.InnerText = 'SomeName'

There are for sure other ways, but this is one is quite easy.


In case that the node would be deeper in xml, you might use Xpath like this (both find first Names node):

$node = (Select-Xml -Xml $x -XPath '//Names[1]').Node
$node = (Select-Xml -Xml $x -XPath '//Names[position()=1]').Node
stej
Erm, you should really get rid of that line mentioning regular expressions in this answer. We've been down that road many tmies before here on SO—it's *not* an option when processing XML.
Joey
Done. Agree with you but for some users this could be option. I could even replace first occurence of `</FirstName>` with `</FirstName><LastName>SomeName</LastName>` and then reformat it as xml. *Hack*
stej
I get this error when running the script:"Property 'InnerText' cannot be found on this object; make sure it exists and is settable."Could it be my version of Powershell?
Draco
If you add `$lastName.GetType().FullName`, what does it return? Also you might try `$lastName.PsBase.InnerText=..`. There were some issues in V1, although I assumed that the proposed solution should work well.
stej
Great. Adding PsBase worked :) But what does it do? Also, how would I remove the xmlns attribute which was added to the element? Adding $lastName.GetType().FullName returns System.Xml.XmlElement
Draco
The workaround with `PsBase` is because powerShell wrapps all the objects into own object (`PSObject`) and sometimes it didn't work properly. `PsBase` is the original object. Do you really need to remove xmlns attr? Because in V2 there is none. Don't you want to switch to V2? Higly recommended! ;)
stej