xmlpowershellcall-by-value

XML function by value issue?


I have a function that returns a hash table with a variety of keys, one of which is XML. I also have some code that is intended to revise this XML before returning it. However, I am having what at first appeared to be pipeline pollution issues, but I have isolated it to the XML replacement code.

So, initially I did all my XML replacement directly with the key in the return hash using $result.xml. However, I have since changed the code to use a separate variable like this:

$revisedXml = $result.xml
$oldnode = $revisedXml.selectSingleNode("/Definitions/InstallTypes/InstallType[@id='$supersedes']")
$newnode = $temp.xml.selectSingleNode("/Definitions/InstallTypes/InstallType[@id='$supersedes']")
$importNode = $revisedXml.ImportNode($newnode, $true)
$oldnode.ParentNode.AppendChild($importNode)
$oldnode.ParentNode.RemoveChild($oldnode)

However, either of those last two lines is what causes the $return hash to blow up. Which has me thinking I think I remember something about XML being by reference not by value, so what I am really doing is directly accessing the XML, just via a new pointer, and what I am changing somehow blows up the whole hash table variable that also references the XML. Does this sound right, or am I going down a wrong path already?

Also, and perhaps related. What I ultimately am looking to do is search for a specific node in $result.xml and in a different $temp.xml (loaded from different files) and if found, replace the one in $result with the one from $temp. I tried using ReplaceChild(), but that seems not to work, perhaps a PowerShell 2.0 issue.

So, with those two bits of data, is there a better way to revise the XML, and is there a way to make it work with a function that properly passes the XML by value? FWIW, I am refactoring to avoid the global XML variable I had been using successfully. I generally don't like global variables, but perhaps this is one situation where I just have to use them?

EDIT: So, trying to isolate the issue, and running into new issues. Here I am trying to create two XML variables in the function, replace a node from one variable with the node from another and pass it back out of the function, then write to console to verify the result is correct (the difference is SilentModeID should be 9). Problem is the two here strings aren't working. Tried with single quotes as well, but still throwing errors. Not really apples to apples as in my real code I am not using here strings, I am reading the XML from files, but for the purposes of discussion it seemed the better way to go. So, first new question, what the heck am I doing wrong with the here strings? Once that's working we can see if passing the XML back out is broken the same way.

function Write-PxXmlToConsole ($xml) {
    $stringWriter = New-Object System.IO.StringWriter
    $xmlWriter = New-Object System.Xml.XmlTextWriter $stringWriter
    $xmlWriter.Formatting = "indented"
    $xml.WriteTo($xmlWriter)
    $xmlWriter.Flush()
    $stringWriter.Flush()
    Write-Host $stringWriter.ToString()
}


function Import-PxXml {

$result = @{
    success = $true
    xml = $null
    log = $null
}

[xml]$assets = @"
<?xml version="1.0"?>
<Definitions>
<InstallTypes>
<InstallType id="deployment">
<SilentModeID>1</SilentModeID>
</InstallTypes>
</InstallType>
</Definitions>
"@

[xml]$newAssets = @"
<?xml version="1.0"?>
<Definitions>
<InstallTypes>
<InstallType id="deployment">
<SilentModeID>9</SilentModeID>
</InstallType>
</InstallTypes>
</Definitions>
"@

$nodeName = 'deployment'

$oldnode = $assets.selectSingleNode("/Definitions/InstallTypes/InstallType[@id='$nodeName']")
$newnode = $newAssets.selectSingleNode("/Definitions/InstallTypes/InstallType[@id='$nodeName']")
$importNode = $assets.ImportNode($newnode, $true)

$oldnode.ParentNode.AppendChild($importNode)
$oldnode.ParentNode.RemoveChild($oldnode)

$result.xml = $assets
$result.log = 'Crossed Fingers'

return $result
}


$test = Import-PxXml
Write-PxXmlToConsole $test.xml

Solution

  • The RemoveChild() and AppendChild() method returns the child node in question, polluting your output.

    Suppress the output and you'll be fine:

    $null = $oldnode.ParentNode.AppendChild($importNode)
    $null = $oldnode.ParentNode.RemoveChild($oldnode)