首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何使用XML:Perl向XMl中的节点添加子元素

如何使用XML:Perl向XMl中的节点添加子元素
EN

Stack Overflow用户
提问于 2015-08-21 05:33:01
回答 1查看 716关注 0票数 3

我试图使用XML:Twig向XML中的节点添加子元素。我的XML是

代码语言:javascript
复制
 <Install>
<version >
<number>6.0</number>
<build>1037124</build>
<path>path</path>
<kind>kind</kind>
</version>
<version >
<number>7.0</number>
<build>1037124</build>
<path>path</path>
<kind>kind</kind>
</version>
</Install>

我想要的输出是:-

代码语言:javascript
复制
 <Install>
<version >
<number>6.0</number>
<build>1037124</build>
<path>path/path>
<kind>kind</kind>
<patch>patch</patch>
<patchv>patchv</patchv>  
</version>
<version >
<number>7.0</number>
<build>1037124</build>
<path>path</path>
<kind>kind</kind>
</version>
</Install>

我想在父版本中添加两个新的子节点,其编号为6.0。

我试过这个代码

代码语言:javascript
复制
    my $version = "6.0";    
    my $file_loc = "data.xml";
    my $twig = XML::Twig->new( pretty_print => 'indented_a' )->parsefile( $file_loc );

for my $number ( $twig->findnodes('/Install/version/number') ) {
  $number->parent->last_child->insert(
              patch => {  version =>'patch' },
              patchversion => {  version =>'patchv' },
        ) if $number->trimmed_text eq $version;
}

但是我得到的输出不是我想要的。产出如下:

代码语言:javascript
复制
<Install>
  <version>
    <number>6.0</number>
    <build>1037124</build>
    <path>path</path>
    <kind>
      <patch version="patch">
    <patchversion version="patchv">kind</patchversion>
      </patch>
    </kind>
  </version>
     <version >
    <number>7.0</number>
    <build>1037124</build>
    <path>path</path>
    <kind>kind</kind>
    </version>
    </Install>

我也尝试过用其他方法使用粘贴,具体如下:

代码语言:javascript
复制
    my $version = "6.0";    
    my $file_loc = "data.xml";


        my $myXML = <<"XML";
<patch>
<build></build>
<version></version>
</patch>
XML
my $t = XML::Twig->new(
  twig_handlers => {
    Install => sub { add_version( $myXML, @_ ); }, }
  )->parsefile( $file_loc ) or die $!;

$t->set_pretty_print('indented_c');
open my $xml_fh, '>', "$file_loc" or die $!;
$t->print($xml_fh);
sub add_version {
    my ( $xml, $t, $install ) = @_;
    my $new_elt = XML::Twig::Elt->parse($xml);
    for my $number ( $t->findnodes('/Install/version/number') ) {
    if($number->trimmed_text eq $version )
    {
    $new_elt->paste(  $number->parent->last_child->paste => $install );
    }
    }
}

在这里,我得到了以下错误

代码语言:javascript
复制
cannot paste an element that belongs to a tree at

我是否可以只用insert来实现这个目标,还是必须使用粘贴??在这两种情况下怎样做??

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-08-21 09:40:20

您的第一次尝试是正确的,但是您需要使用insert_new_elementinsert将替换元素的内容,并将使用您选择的语法创建属性。

您可以将其放在一个非常简单的twig_handler中。获取所有<version>元素,检查它们的<number>文本节点,然后为修补程序和patchv值添加一个带有文本节点的新元素。

last_child => ''部件将新元素放在父元素的末尾。是paste

代码语言:javascript
复制
use strict;
use warnings;
use XML::Twig;

my $twig = XML::Twig->new(
    pretty_print  => 'indented_a',
    twig_handlers => {
        'version' => sub {
            if ( $_->first_child('number')->text() eq '6.0' ) {
                $_->insert_new_elt( last_child => 'patch' )->set_text('patch');
                $_->insert_new_elt( last_child => 'patchv' )->set_text('patchv');
            }
        }
    }
)->parse( \*DATA );

$twig->print;

输出:

代码语言:javascript
复制
<Install>
  <version>
    <number>6.0</number>
    <build>1037124</build>
    <path>path</path>
    <kind>kind</kind>
    <patch>patch</patch>
    <patchv>patchv</patchv>
  </version>
  <version>
    <number>7.0</number>
    <build>1037124</build>
    <path>path</path>
    <kind>kind</kind>
  </version>
</Install>

您的第二次尝试不起作用至少有两个原因:

代码语言:javascript
复制
# this elem get's pasted (1)
# |               on this positon (2)                            
# |               |                                     in that elem (3)
$new_elt->paste(  $number->parent->last_child->paste => $install );

在(2)中,它应该有一个位置,即字符串。就像first_childlast_child等等。paste

因此,它将计算$number->parent->last_child->paste的返回值,并将其用作位置字符串。但是没有这样的返回值,所以这是行不通的。

,但它没有走那么远,。错误消息无法粘贴属于树的元素,该元素来自您在(2)中拥有的paste,因为您正在$number->parent->last_child上调用paste(),并且该元素已经在树中。这是不允许的如果删除该部分,代码将编译。

代码语言:javascript
复制
if ( $number->trimmed_text eq $version ) {
  $new_elt->paste( $install );
}

输出是(剪短的):

代码语言:javascript
复制
<Install>
  <patch>
    <build></build>
    <version></version>
  </patch>
  <version>
    <number>6.0</number>
    <build>1037124</build>
    <path>path</path>
    <kind>kind</kind>
  </version>

这仍然不是我们想要的,因为<patch>最终在<Install>中结束,而不是在<version>中。但是我们已经有了循环中的$number。这就是<number>节点中的<version>节点,因此我们可以获取它的父节点并在我们的(3)中使用它。

代码语言:javascript
复制
$new_elt->paste( $number->parent );

现在我们明白了:

代码语言:javascript
复制
<Install>
  <version>
    <patch>
      <build></build>
      <version></version>
    </patch>
    <number>6.0</number>
    <build>1037124</build>
    <path>path</path>
    <kind>kind</kind>
  </version>

那好多了。在合适的地方。如果您添加的位置last_child,它结束在末尾。

代码语言:javascript
复制
$new_elt->paste( last_child => $number->parent );

现在它在这里:

代码语言:javascript
复制
<Install>
  <version>
    <number>6.0</number>
    <build>1037124</build>
    <path>path</path>
    <kind>kind</kind>
    <patch>
      <build></build>
      <version></version>
    </patch>
  </version>

现在它只是我们粘贴的错误的东西,因为我们想要两个元素<patch><patchv>,都带有文本。那是因为你在$myXML里有那些。

您不能在那里有两个顶级元素。这是无效的XML语法。

代码语言:javascript
复制
my $myXML = <<"XML";
<patch>patch</patch>
<patchv>patchv</patchv>
XML

这将失败的垃圾后文档元素在第2行,第0列,字节21在.因此,您必须有两个变量并两次调用paste,或者包装它们并获取新根元素的子节点。

当然,这是非常复杂的。上面的第一种方法是非常干净的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32132993

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档