Search code examples
xmlpowershellparsingelementinsertafter

How to insert element after a specific element in XML with insertAfter()


My file .xml looks like this

<?xml version="1.0" encoding="UTF-8"?>
<layoutMaster xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="..\schema\m.xsd">
  <deviceFamily>
    <deviceFamilyUniqueID>DeviceFamilyWayside_2</deviceFamilyUniqueID>
    <name>DeviceFamilyWayside_2</name>
    <device>SAMSUNG</device>
    <device>SONY</device>
    <deviceFamilyOptions>
      <name>false</name>
    </deviceFamilyOptions>
  </deviceFamily>
  <deviceFamily>
    <deviceFamilyUniqueID>DeviceFamilyWayside_4</deviceFamilyUniqueID>
    <name>DeviceFamilyWayside_4</name>
    <device>IPHONE</device>
    <device>MAC</device>
    <deviceFamilyOptions>
      <name>false</name>
    </deviceFamilyOptions>
  </deviceFamily>
</layoutMaster>

And I would like to add a new device in deviceFamily[deviceFamilyUniqueID ='DeviceFamilyWayside_2'] after the last device:

So I tried this following code in PowerShell:

$Path = ".\config.xml"
[xml]$xmlFile = Get-Content -Path $Path

$xmlNewElementDevice = $xmlFile.CreateElement("device")
$xmlNewElementDevice.AppendChild($xmlFile.CreateTextNode("ALCATEL"))

$xmlTargetDeviceWayside = $xmlFile.SelectSingleNode('//layoutMaster/deviceFamily[deviceFamilyUniqueID = "DeviceFamilyWayside_2"]')
$xmlTargetDeviceWayside.InsertAfter($xmlNewElementDevice,$xmlTargetDeviceWayside.name)

but I got this error:

Unable to convert the argument "1" (value "DeviceFamilyWayside_2") from "InsertAfter" to type "System.Xml.XmlNode":" Unable to convert "DeviceFamilyWayside_2" value from "System.String" to type "System.Xml.XmlNode". "

The final result I would like to get is like this:

<?xml version="1.0" encoding="UTF-8"?>
<layoutMaster xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="..\schema\m.xsd">
  <deviceFamily>
    <deviceFamilyUniqueID>DeviceFamilyWayside_2</deviceFamilyUniqueID>
    <name>DeviceFamilyWayside_2</name>
    <device>SAMSUNG</device>
  <device>SONY</device>
    <device>ALCATEL</device>    <--------------- here
    <deviceFamilyOptions>
      <name>false</name>
    </deviceFamilyOptions>
  </deviceFamily>
  <deviceFamily>
    <deviceFamilyUniqueID>DeviceFamilyWayside_4</deviceFamilyUniqueID>
    <name>DeviceFamilyWayside_4</name>
    <device>IPHONE</device>
  <device>MAC</device>
    <deviceFamilyOptions>
      <name>false</name>
    </deviceFamilyOptions>
  </deviceFamily>
</layoutMaster>

Solution

  • Insert the new node before the node <deviceFamilyOptions>:

    [xml]$xml = Get-Content -Path $Path
    
    $newNode = $xml.CreateElement("device")
    $newNode.InnerText = 'ALCATEL'
    
    $parent = $xml.SelectSingleNode('//layoutMaster/deviceFamily[deviceFamilyUniqueID="DeviceFamilyWayside_2"]')
    $ref    = $parent.SelectSingleNode('./deviceFamilyOptions')
    
    $parent.InsertBefore($newNode, $ref) | Out-Null
    

    Alternatively select the last <device> node as the reference node after which to insert the new node:

    $parent = $xml.SelectSingleNode('//layoutMaster/deviceFamily[deviceFamilyUniqueID="DeviceFamilyWayside_2"]')
    $ref    = $parent.SelectSingleNode('./device[last()]')
    
    $parent.InsertAfter($newNode, $ref) | Out-Null