Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.4k views
in Technique[技术] by (71.8m points)

powershell - Merge XML documents into empty XML node?

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!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:pathocustomers.xml')

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...