How to use InsertAfter with PowerShell

I have xml files where I want to paste the contents of one xml file into another. I decided to use LastChild and the InsertAfter method for this. So far this does not work for me.

Here is the parent.xml file:

<manifest> <manifestExecution> <assetDetail> <fileAsset fileAssetGuid="parentguid1"> <parentfile1 /> </fileAsset> <fileAsset fileAssetGuid="parentguid2"> <parentfile2 /> </fileAsset> </assetDetail> </manifestExecution> </manifest> 

And here is the child.xml file:

 <manifest> <manifestExecution> <assetDetail> <fileAsset fileAssetGuid="childguid1"> <childfile1 /> </fileAsset> </assetDetail> </manifestExecution> </manifest> 

What I want to do is select the Asset node file from child.xml and paste it into parent.xml after the last fileAsset node file in parent.xml.

Here is my test code:

 $parent = [xml] (Get-Content d:\temp\parent.xml) $parentnode = $parent.manifest.manifestExecution.assetDetail $child = [xml] (Get-Content d:\temp\child.xml) $childnode = $child.manifest.manifestExecution.assetDetail.InnerXml $parentnode.InsertAfter($childnode, $parentnode.LastChild) 

Here is the msg message:

Cannot convert argument "0", with value: "<fileAsset fileAssetGuid="childguid1"> <childfile1 /></fileAsset>", for "InsertAfter" to type "System.Xml.XmlNode": "Cannot conver t the "<fileAsset fileAssetGuid="childguid1"><childfile1 /></fileAsset>" value of type "System.String" to type "System.Xml.XmlNode"." At line:5 char:24 + $parentnode.InsertAfter <<<< ($childnode, $parentnode.LastChild) + CategoryInfo : NotSpecified: (:) [], MethodException + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

What am I doing wrong?

+4
source share
2 answers

You need to iterate through the $childnode children, remove them from their parent and import them into the new document context ( $child and $parent are different instances of the XmlDocument ) before adding them to $parentnode .

This will add all fileAsset nodes from $childnode to $parentnode .

 $parent = [xml](get-content d:\temp\parent.xml) $parentnode = $parent.manifest.manifestexecution.assetdetail $child = [xml](get-content d:\temp\child.xml) $childnode = $child.manifest.manifestexecution.assetdetail while ($childnode.haschildnodes) { $cn = $childnode.firstchild $cn = $childnode.removechild($cn) $cn = $parentnode.ownerdocument.importnode($cn, $true) $parentnode.appendchild($cn) } 

Fortunately, most of these methods return the same XmlNode or new version, so the body of the while can be copied as follows:

 $parentnode.appendchild( $parentnode.ownerdocument.importnode( $childnode.removechild( $childnode.firstchild ), $true )) 

InsertAfter(newChild,referenceChild) can also work, but it will be executed slightly differently, since it also needs a link to the node that it will be inserted after.

+6
source

Your first problem is that you are not getting an XML element, but a string. You need to get the XML node from your XML document, but the shorthand method you use assumes you want a string. You can usually force it to redirect to [System.Xml.XmlElement], but this does not always work. You can reliably get the item using "SelectSingleNode".

You have not hit your second problem yet, but it is not far off. When you have XML, it still wonโ€™t work, because it is from another XML document, so you need to โ€œImportโ€ the node. You will want to tweak it so that the XML matches the way you imagine it, but the code works.

 $parentString = @" <manifest> <manifestExecution> <assetDetail> <fileAsset fileAssetGuid="parentguid1"> <parentfile1 /> </fileAsset> <fileAsset fileAssetGuid="parentguid2"> <parentfile2 /> </fileAsset> </assetDetail> </manifestExecution> </manifest> "@ $childString = @" <manifest> <manifestExecution> <assetDetail> <fileAsset fileAssetGuid="childguid1"> <childfile1 /> </fileAsset> </assetDetail> </manifestExecution> </manifest> "@ $parent = [xml] ($parentString) $parentnode = $parent.manifest.manifestExecution.assetDetail $child = [xml] ($childString) $xpath = '/manifest/manifestExecution/assetDetail' $childnode = $child.SelectSingleNode($xpath) Write-Host("So the child is $($childnode.OuterXML)") $importedNode = $parent.ImportNode($childNode,$true) Write-Host("And after importing: $($importedNode.OuterXML)") $parentnode.InsertAfter($importednode, $parentnode.LastChild) Write-Host("To finally yield: $($parent.OuterXML)") 

In addition, you may find that you can use something like your source code if you applied it correctly to the XmlElement.

 $childnode = [System.Xml.XmlElement]$child.manifest.manifestExecution.assetDetail.InnerXml 
0
source

All Articles