如何使用Perl在XML数据中移动节点

时间:2014-10-30 19:46:10

标签: perl xml-libxml

我尝试了几种不同的方法来解析XML文档。我需要在文档中找到一个带有x属性的节点,然后将该节点及其子节点移动到另一个父节点。

我使用XML::LibXML找到节点并能够遍历它的子节点时取得了一些成功,但我仍然坚持如何将它移动到另一个父节点。

使用$node->cloneNode(1)看起来很有希望,但我找不到好用的例子。

这是原始的XML数据

<config logdir="/var/log/foo/" debugfile="/tmp/foo.debug">
  <old>
    <server name="sahara" osname="solaris" osversion="2.6">
      <address ip="10.0.0.101">Private</address>
      <address ip="10.0.1.101">Private</address>
    </server>
    <server name="gobi" osname="irix" osversion="6.5">
      <address ip="10.0.0.102">Private</address>
    </server>
    <server name="kalahari" osname="linux" osversion="2.0.34">
      <address ip="10.0.0.103">Private</address>
      <address ip="10.0.1.103">Private</address>
    </server>
  </old>
  <new>
  </new>        
</config>

这是我想要的结果

<config logdir="/var/log/foo/" debugfile="/tmp/foo.debug">
  <old>
    <server name="sahara" osname="solaris" osversion="2.6">
      <address ip="10.0.0.101">Private</address>
      <address ip="10.0.1.101">Private</address>
    </server>
    <server name="gobi" osname="irix" osversion="6.5">
      <address ip="10.0.0.102">Private</address>
    </server>
  </old>
  <new>
    <server name="kalahari" osname="linux" osversion="2.0.34">
      <address ip="10.0.0.103">Private</address>
      <address ip="10.0.1.103">Private</address>
    </server>
  </new>        
</config>

2 个答案:

答案 0 :(得分:2)

使用XML::LibXML

use strict;
use warnings;

use XML::LibXML;

my $xml = XML::LibXML->load_xml( IO => \*DATA );

my ($new) = $xml->findnodes('//new');

for my $linux ( $xml->findnodes('//old/server[@osname="linux"]') ) {
    $linux->unbindNode();         # Remove from Parent (done automatically when added elsewhere without cloning)
    $new->addChild($linux);
}

print $xml;

__DATA__
<config logdir="/var/log/foo/" debugfile="/tmp/foo.debug">
    <old>
        <server name="sahara" osname="solaris" osversion="2.6">
            <address ip="10.0.0.101">Private</address>
            <address ip="10.0.1.101">Private</address>
        </server>
        <server name="gobi" osname="irix" osversion="6.5">
            <address ip="10.0.0.102">Private</address>
        </server>
        <server name="kalahari" osname="linux" osversion="2.0.34">
            <address ip="10.0.0.103">Private</address>
            <address ip="10.0.1.103">Private</address>
        </server>
    </old>
    <new>
    </new>      
</config>

输出:

<?xml version="1.0"?>
<config logdir="/var/log/foo/" debugfile="/tmp/foo.debug">
    <old>
        <server name="sahara" osname="solaris" osversion="2.6">
            <address ip="10.0.0.101">Private</address>
            <address ip="10.0.1.101">Private</address>
        </server>
        <server name="gobi" osname="irix" osversion="6.5">
            <address ip="10.0.0.102">Private</address>
        </server>

    </old>
    <new>
    <server name="kalahari" osname="linux" osversion="2.0.34">
            <address ip="10.0.0.103">Private</address>
            <address ip="10.0.1.103">Private</address>
        </server></new>      
</config>

答案 1 :(得分:1)

您需要做的就是找到子节点和父节点,并通过调用appendChild将子节点移动到新父节点。

假设您的原始XML数据位于名为config.xml的文件中,代码将如下所示。

我不知道更好的方法来说服XML::LibXML生成精美缩进的数据。我发现最好的是启用no_blanks并使用非零参数toString,但正如您所看到的那样,它远非理想。

use strict;
use warnings;

use XML::LibXML;

my $xml = XML::LibXML->load_xml(location => 'config.xml', no_blanks => 1);

my ($kalahari) = $xml->findnodes('/config/old/server[@name="kalahari"]');

my ($new) = $xml->findnodes('/config/new');

$new->appendChild($kalahari);

print $xml->toString(1);

<强>输出

<?xml version="1.0"?>
<config logdir="/var/log/foo/" debugfile="/tmp/foo.debug">
  <old>
    <server name="sahara" osname="solaris" osversion="2.6">
      <address ip="10.0.0.101">Private</address>
      <address ip="10.0.1.101">Private</address>
    </server>
    <server name="gobi" osname="irix" osversion="6.5">
      <address ip="10.0.0.102">Private</address>
    </server>
  </old>
  <new>
  <server name="kalahari" osname="linux" osversion="2.0.34"><address ip="10.0.0.103">Private</address><address ip="10.0.1.103">Private</address></server></new>
</config>