1

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!

0

2 Answers 2

4

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')
Sign up to request clarification or add additional context in comments.

Comments

2

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)

1 Comment

Thanks, the $doc.ChildNodes | ? { $_.name -eq "customers" } part was what I needed. The example I gave wasn't quite one-for-one the XML that I'm working with, but it was enough to resolve the problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.