简体   繁体   中英

adding XML sub-elements

With PowerShell, I want to add several sub-elements into an XML tree.
I know to ADD ONE element, I know to add one or several attributes, but I don't understand how to ADD SEVERAL elements.

One way whould be to write a sub-XML tree as text
But I can't use this method because the elements are not added at once.

To add one element, I do that:

[xml]$xml = get-content $nomfichier
$newEl = $xml.CreateElement('my_element')
[void]$xml.root.AppendChild($newEl)

Works fine. This give me this XML tree:

$xml | fc
class XmlDocument
{
  root =
    class XmlElement
    {
      datas =
        class XmlElement
        {
          array1 =
            [
              value1
              value2
              value3
            ]
        }
      my_element =     <-- the element I just added
    }
}

Now I want to add a sub element to 'my_element'. I use a similar method:

$anotherEl = $xml.CreateElement('my_sub_element')
[void]$xml.root.my_element.AppendChild($anotherEl) <-- error because $xml.root.my_element is a string
[void]$newEl.AppendChild($anotherEl)               <-- ok
$again = $xml.CreateElement('another_one')
[void]$newEl.AppendChild($again)

This give this XML tree (partialy displayed):

my_element =
  class XmlElement
  {
    my_sub_element =
    another_one =
  }

Those are attributes, not sub-elements.
Sub-elements would be displayed as this:

my_element =
  [
    my_sub_element
    another_one
  ]

Question : How do I add several sub-elements, one at a time?

Have a look to the following example :

# Document creation
[xml]$xmlDoc = New-Object system.Xml.XmlDocument
$xmlDoc.LoadXml("<?xml version=`"1.0`" encoding=`"utf-8`"?><Racine></Racine>")

# Creation of a node and its text
$xmlElt = $xmlDoc.CreateElement("Machine")
$xmlText = $xmlDoc.CreateTextNode("Mach1")
$xmlElt.AppendChild($xmlText)

# Creation of a sub node
$xmlSubElt = $xmlDoc.CreateElement("Adapters")
$xmlSubText = $xmlDoc.CreateTextNode("Network")
$xmlSubElt.AppendChild($xmlSubText)
$xmlElt.AppendChild($xmlSubElt)

# Creation of an attribute in the principal node
$xmlAtt = $xmlDoc.CreateAttribute("IP")
$xmlAtt.Value = "128.200.1.1"
$xmlElt.Attributes.Append($xmlAtt)

# Add the node to the document
$xmlDoc.LastChild.AppendChild($xmlElt);

# Store to a file 
$xmlDoc.Save("c:\Temp\Temp\Fic.xml")

Edited

Remark : Using a relative path in Save will not do what you expect .

I prefer creating xml by hand, instead of using API to construct it node by node, as imho by hand it will be much more readable and more maintable.

Here is an example:

$pathToConfig = $env:windir + "\Microsoft.NET\Framework64\v4.0.30319\Config\web.config"

$xml = [xml] (type $pathToConfig)

[xml]$appSettingsXml = @"
<appSettings>
    <add key="WebMachineIdentifier" value="$webIdentifier" />
</appSettings>
"@


$xml.configuration.AppendChild($xml.ImportNode($appSettingsXml.appSettings, $true))
$xml.Save($pathToConfig)

Check this code-sample. It has everything you need to create XML from scratch:

function addElement($e1, $name2, $value2, $attr2)
{
    if ($e1.gettype().name -eq "XmlDocument") {$e2 = $e1.CreateElement($name2)}
    else {$e2 = $e1.ownerDocument.CreateElement($name2)}
    if ($attr2) {$e2.setAttribute($value2,$attr2)}
    elseif ($value2) {$e2.InnerText = "$value2"}
    return $e1.AppendChild($e2)
}

function formatXML([xml]$xml)
{
    $sb = New-Object System.Text.StringBuilder
    $sw = New-Object System.IO.StringWriter($sb)
    $wr = New-Object System.Xml.XmlTextWriter($sw)
    $wr.Formatting = [System.Xml.Formatting]::Indented
    $xml.Save($wr)
    return $sb.ToString()
}

...now let's use both functions to create and display a new XML-object:

$xml = New-Object system.Xml.XmlDocument
$xml1 = addElement $xml "a"
$xml2 = addElement $xml1 "b"
$xml3 = addElement $xml2 "c" "value"
$xml3 = addElement $xml2 "d" "attrib" "attrib_value"

write-host `nFormatted XML:`r`n`n(formatXML $xml.OuterXml)

the result looks like this:

Formatted XML:

 <?xml version="1.0" encoding="utf-16"?>
<a>
  <b>
    <c>value</c>
    <d attrib="attrib_value" />
  </b>
</a>

For anyone else visiting this.

I had issues because my parent document had a namespace, and the ImportNode was adding an empty xmnls="" element to the imported xml, causing issues with my app

Extending on answer above. To get around this, wrap it in a dummy node, with namespace set from parent doc

$pathToConfig = $env:windir + "\Microsoft.NET\Framework64\v4.0.30319\Config\web.config"

$xml = [xml] (type $pathToConfig)
$root = $xml.get_DocumentElement()
$namespaceuri = $root.NamespaceURI

[xml]$appSettingsXml = @"
<Dummy xmlns="$namespaceuri">
    <appSettings>
        <add key="WebMachineIdentifier" value="$webIdentifier" />
    </appSettings>
</Dummy>
"@


$xml.configuration.AppendChild($xml.ImportNode($appSettingsXml.Dummy.appSettings, $true))
$xml.Save($pathToConfig)

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