Reputation: 6491
EDIT: This isn't quite the same as the posted that was linked. The main problem I was running into was appending child nodes to an empty XML node. When selecting the node directly, it would return a System.String
type, which doesn't have the AppendChild
method. The fix was to select all child nodes and then filter that by name, per Dan's suggestion below.
$emptyNode= $root.ChildNodes | ? { $_.name -eq "customers" }
I'm mostly using powershell, but underneath a lot of the code I'm working with is using .NET System object. What I'm looking to do is probably better explained through an example. Say I have three XML documents:
<!-- XML File A -->
<customer>
<name>ACME Co</name>
<users>
<user>
<name>Alex</name>
<age>20</age>
</user>
<user>
<name>Aaron</name>
<age>21</age>
</user>
<user>
<name>Allison</name>
<age>22</age>
</user>
</users>
</customer>
and
<!-- XML File B -->
<customer>
<name>Big Co</name>
<users>
<user>
<name>Bob</name>
<age>30</age>
</user>
<user>
<name>Barry</name>
<age>31</age>
</user>
<user>
<name>Bill</name>
<age>32</age>
</user>
</users>
</customer>
and
<!-- XML File C -->
<customer>
<name>Cool Co</name>
<users>
<user>
<name>Carl</name>
<age>40</age>
</user>
<user>
<name>Craig</name>
<age>41</age>
</user>
<user>
<name>Chris</name>
<age>42</age>
</user>
</users>
</customer>
I have a "root" document that looks like:
<?xml version='1.0' encoding='utf-8' ?>
<customers>
</customers>
I want to combine the three A, B, and C documents under the root doc so my final product will be:
<?xml version='1.0' encoding='utf-8' ?>
<customers>
<!-- XML File A -->
<customer>
<name>ACME</name>
<users>
<user>
<name>Alex</name>
<age>20</age>
</user>
<user>
<name>Aaron</name>
<age>21</age>
</user>
<user>
<name>Allison</name>
<age>22</age>
</user>
</users>
</customer>
<!-- XML File B -->
<customer>
<name>Big Co</name>
<users>
<user>
<name>Bob</name>
<age>30</age>
</user>
<user>
<name>Barry</name>
<age>31</age>
</user>
<user>
<name>Bill</name>
<age>32</age>
</user>
</users>
</customer>
<!-- XML File C -->
<customer>
<name>Cool Co</name>
<users>
<user>
<name>Carl</name>
<age>40</age>
</user>
<user>
<name>Craig</name>
<age>41</age>
</user>
<user>
<name>Chris</name>
<age>42</age>
</user>
</users>
</customer>
</customers>
I've been looking at AppendChild and ImportNode but I keep getting various errors. One thing, is that in my root document, the single empty node customers
is listed a type of System.String rather than an XmlNode, so I can't append any children. See this Powershell snippet:
$doc = New-Object System.Xml.XmlDocument
$doc.LoadXml("<?xml version='1.0' encoding='utf-8' ?><customers></customers>")
$doc.customers.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
That doesn't even matter that much though, because if I try to import the node, I get an error Cannot import nodes of type 'Document'.
.
$docA = New-Object System.Xml.XmlDocument
$docA.LoadXml("<customer><name>ACME</name><users><user><name>Alex</name><age>20</age></user><user><name>Aaron</name><age>21</age></user><user><name>Allison</name><age>22</age></user></users></customer>")
$docAImported = $doc.ImportNode($docA, $true)
Exception calling "ImportNode" with "2" argument(s): "Cannot import nodes of type 'Document'."
At line:1 char:32
+ $docAImported = $doc.ImportNode <<<< ($docA, $true)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
Any help would be greatly appreciated. Thanks!
Upvotes: 1
Views: 1237
Reputation: 200473
If you need to work with nodes from an XML tree I'd recommend to select the node(s) via SelectSingleNode()
or SelectNodes()
and an XPath expression:
[xml]$doc = "<?xml version='1.0' encoding='utf-8' ?><customers></customers>"
$root = $doc.SelectSingleNode('/customers')
Then you can import and append the nodes from your other XML files like this:
Get-ChildItem '*.xml' | % {
[xml]$xml = Get-Content $_.FullName
$node = $xml.SelectSingleNode('/customer')
$importedNode = $doc.ImportNode($node, $true)
$root.AppendChild($importedNode)
}
$doc.Save('C:\path\to\customers.xml')
Upvotes: 4
Reputation: 2024
Get the customer node first by doing the following:
$customersNode = $doc.ChildNodes | ? { $_.name -eq "customers" }
Now you can call AppendChild on $customersNode with document A, B and C.
However to import document A, B and C you almost had it right. Use the DocumentElement property like below:
$docAImported = $doc.ImportNode($docA.DocumentElement, $true)
Upvotes: 2