Replacing XML Node

This topic contains 7 replies, has 3 voices, and was last updated by Profile photo of Chris Cass Chris Cass 1 week, 5 days ago.

  • Author
    Posts
  • #70382
    Profile photo of Chris Cass
    Chris Cass
    Participant

    I'm trying to write a script to replace a node on a single XML file. My XML example can be seen here:

    I'm assuming I need to first figure out how to remove the existing node, then add the new one... But I'm stuck on the removal. This is what I currently have:

    $path = "C:\Testing\File.xml"
    
    [xml]$servicefactoryconfig = gc $path
    
    $AuditOut = $servicefactoryconfig.SelectSingleNode("factory.map.add.Key[.='Audit']")
    $AuditOut.parentnode.removeall()
    $servicefactoryconfig.save($path)
    

    When I run this, Notepad++ is telling me the file has been edited, but there is no difference in the file. I am also seeing this error:

    You cannot call a method on a null-valued expression.
    At line:14 char:1
    + $AuditOut.parentnode.removeall()
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    Anyone have any suggestions? Is there an easier way to just replace this instead of removing, saving, and adding?

  • #70402
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    You can try something like this:

    #original XML content
    [xml]$myXml = @"
    
    
      
        
          
        
      
    
    "@
    
    #Build an object with your values
    $config = @()
    $config += [pscustomobject]@{name="baseUrl";class="string";value="http://hostname/Website"}
    $config += [pscustomobject]@{name="servicePath";class="string";value="Audit/AuditASMX.asmx"}
    $config += [pscustomobject]@{name="timeout";class="int";value="120"}
    $config += [pscustomobject]@{name="policy";class="string";value="AuditServicePolicy"}
    
    #Select Node
    $element = $myXML.factory.map.add
    #Remove Node
    $element.RemoveChild($element.FirstChild)
    
    #$element.SelectSingleNode()
    foreach ($item in $config) {
        #
        $child = $myXML.CreateElement("parameter")
        
        #Name
        $addName = $myXML.CreateAttribute("name")
        $addName.Value = $item.Name
        $child.Attributes.Append($addName)
    
        #Class
        $addClass = $myXML.CreateAttribute("Class")
        $addClass.Value = $item.Class
        $child.Attributes.Append($addClass)
    
        #Value
        $addValue = $myXML.CreateAttribute("Value")
        $addValue.Value = $item.Value
        $child.Attributes.Append($addValue)
        
        #Append to Add
        $element.AppendChild($child)
    }
    
    $myXML.Save(("{0}\myxml.xml" -f [environment]::GetFolderPath("Desktop")))
    
  • #70405
    Profile photo of Daniel Krebs
    Daniel Krebs
    Moderator

    I think I found the problem. Your XPath was not valid and yes the previous node needs to be removed.
    What do you think about the following?

    • #70409
      Profile photo of Chris Cass
      Chris Cass
      Participant

      You sir, are a rock star! Worked like a charm! THANK YOU!!!

    • #70426
      Profile photo of Chris Cass
      Chris Cass
      Participant

      Daniel – upon further investigation, a number of my existing "add" nodes are missing the closing tag after I run the file. And they don't seem to be related in any way to what we're adding or removing. It's like the entire node is still there, it's just missing the /add at the end. Any idea what might cause that?

      Edit: I just looked over the file. It appears it removed the closing tag on any add node that does not have a child node. If there is a child node, the closing tag was not removed.

    • #70492
      Profile photo of Chris Cass
      Chris Cass
      Participant

      Upon further investigation, this appears to be working... I had started by making a backup of the file and changed the file type to *.original instead of *.xml. I then compared the changed file with the original... Apparently CompareIt doesn't like to display XML files properly. As soon as I changed the *.original to *.xml and compared 2 xml files, both files were identical.

      Thanks again for the help!

  • #70408
    Profile photo of Chris Cass
    Chris Cass
    Participant

    Thanks Rob – a couple of questions. Admitting this is still fairly new to me, but I'm looking this over trying to figure out where the file path to my xml file should be going in here...

    Also, I probably should have mentioned this, but there are many "add" nodes in this file, and this particular node can be in any location on the file. From the look of the code it's trying to remove the "first" add node. So it needs to be specific to the one that has the name "audit." So would I format that:

    $element.RemoveChild($element.add | where-object key -eq "audit")

    ?

  • #70411
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    Daniel showed an example using xpath to specify a certain element, this is more dot notation. Yes, you can specifically reference a child using a where clause like such:

    $element.RemoveChild(($element.parameter | Where{$_.value -like "*audit*"}))
    

You must be logged in to reply to this topic.