【问题标题】:How to move a node in XML data using Perl如何使用 Perl 在 XML 数据中移动节点
【发布时间】:2014-10-30 19:46:10
【问题描述】:

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

我使用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>

【问题讨论】:

  • 你所说的“具有'x'属性的节点”是什么意思?您是说要移动具有name 属性kalahariserver 元素吗?
  • 请显示您尝试过的 Perl 代码
  • 优秀。太棒了。谢谢你们俩!是的 Borodin,就像在这个例子中一样,匹配一些属性,比如 name 或 ip。我没有移动的示例代码,因为我不知道从哪里开始:/我绝对可以用这个运行。再次感谢!

标签: perl xml-libxml


【解决方案1】:

使用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>

【讨论】:

  • 不用unbindNode
  • 是的。我注意到在定位之后,但认为演示如何显式从父级中删除仍然很有帮助。添加了明确的评论
  • 问题:my () 在声明时有什么意义:my ($new) = $xml-&gt;findnodes('//new'); 而不是 my $new
  • findnodes 返回一个列表。为了获取列表的第一个元素而不是匹配节点的计数,还需要将左侧放入列表上下文中。
  • 知道了。谢谢米勒!
【解决方案2】:

您需要做的就是找到子节点和父节点,然后通过调用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>

【讨论】:

  • no_blanks 主要是一个黑客。要获得格式良好的输出,XML::LibXML::PrettyPrint 可能是最佳选择。
  • @nwellnhof:我看过了,但我不明白为什么XML::LibXML 不能自己做。我不喜欢有一个完整的其他模块可以有效地解决问题。顺便说一下链接是XML::LibXML::PrettyPrint
猜你喜欢
  • 1970-01-01
  • 2013-07-15
  • 1970-01-01
  • 2011-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-28
  • 1970-01-01
相关资源
最近更新 更多