XML::Twig change tag或者create element based on node existence

时间:2022-01-19 01:53:16

I have a XML (example) file: test.xml

我有一个XML(示例)文件:test.xml。

<root>
   <tag1>AAA</tag1>
   <tag2>BBB</tag2>
   <tag3>
      <tag4>DDD</tag4>
   </tag3>
</root>

The result I want to achieve is, set two variables (from input): i.e.:

我想要达到的结果是,设置两个变量(来自输入):即:

my $xpath = '/root/tag3/tag4';   # or '/root/tag2/tag5' or '/root/tag6'
my $xvalue = 'CCC';              # or 'EEE'

The script would check the $xpath variable, if it exists in the XML file, then it changes the text of it. If it doesn't exist in the XML file, then it creates the element with $xpath and $xvalue.

脚本将检查$xpath变量,如果它存在于XML文件中,那么它将更改它的文本。如果它不存在于XML文件中,则使用$xpath和$xvalue创建元素。

I use below script to set the text for $xpath, but how to modify it so that it would do proper things based on the $xpath existence? Thanks a lot,

我使用下面的脚本设置$xpath的文本,但是如何修改它,以便它能够根据$xpath的存在进行适当的操作?非常感谢,

open( my $output, '>', "$ofile") or die "cannot create $ofile: $!";
XML::Twig->new( twig_roots => { "$xpath" =>
                               sub { my $text= $_->text();
                                     $_->set_text($xvalue);
                                     $_->flush;
                                   },
                             },
            twig_print_outside_roots => $output,
            pretty_print => 'indented',
          )
          ->parsefile( "test.xml" );

1 个解决方案

#1


4  

It's a fairly simple task using a recursive subroutine

这是一个使用递归子例程的相当简单的任务

In the program below, each call to add_xpath advances the value of $node and removes one step from the XPath expression in the $path parameter

在下面的程序中,每个对add_xpath的调用都会提高$node的值,并从$path参数中的XPath表达式中移除一步。

  • If the path begins with a slash and a tag name then the tag name is checked to make sure it matches the name of the root element. Then the current node is set to the root element and the subroutine recurses

    如果路径以斜线和标记名开头,则检查标记名,以确保它与根元素的名称匹配。然后将当前节点设置为根元素,然后将子例程递归

  • If the path starts immediately with a tag name, then has_child is called to see if a child of that name already exists. If not then insert_new_elt adds one for us. The current node is set to the new or pre-existing child node and the subroutine recurses

    如果路径立即以标记名开始,则调用has_child来查看该名称的子节点是否已经存在。如果没有,那么insert_new_elt为我们添加一个。当前节点被设置为新的或已存在的子节点,子例程递归

  • Otherwise the path should be empty, and it is checked to make sure. Then set_text is called to set the text contents of the currenty node and the recursion terminates

    否则路径应该为空,并检查它以确保。然后调用set_text来设置currenty节点的文本内容,递归结束

The output show the resulting XML structure after each of the three operations that you show in your question

输出显示了问题中显示的三个操作中的每个操作之后的结果XML结构

use strict;
use warnings;

use XML::Twig;
use Carp;

my $twig = XML::Twig->new;
$twig->parsefile('test.xml');
$twig->set_pretty_print('indented');
print $twig->sprint, "\n";

add_xpath($twig->root, '/root/tag3/tag4', 'CCC');
print $twig->sprint, "\n";

add_xpath($twig->root, '/root/tag2/tag5', 'EEE');
print $twig->sprint, "\n";

add_xpath($twig->root, '/root/tag6', 'GGG');
print $twig->sprint, "\n";

sub add_xpath {
    my ($node, $path, $value) = @_;

    if ( $path =~ s|^/(\w+)/?|| ) {
        my $tag = $1;
        $node = $node->root;
        carp "Root element has wrong tag name" unless $node->tag eq $tag;
    }
    elsif ( $path =~ s|^(\w+)/?|| ) {
        my $tag = $1;
        if ( my $child = $node->has_child($tag) ) {
            $node = $child;
        }
        else {
            $node = $node->insert_new_elt('last_child', $tag);
        }
    }
    else {
        carp qq{Invalid path at "$path"} if $path =~ /\S/;
        $node->set_text($value);
        return 1;
    }

    add_xpath($node, $path, $value);
}

output

<root>
  <tag1>AAA</tag1>
  <tag2>BBB</tag2>
  <tag3>
    <tag4>DDD</tag4>
  </tag3>
</root>

<root>
  <tag1>AAA</tag1>
  <tag2>BBB</tag2>
  <tag3>
    <tag4>CCC</tag4>
  </tag3>
</root>

<root>
  <tag1>AAA</tag1>
  <tag2>BBB<tag5>EEE</tag5></tag2>
  <tag3>
    <tag4>CCC</tag4>
  </tag3>
</root>

<root>
  <tag1>AAA</tag1>
  <tag2>BBB<tag5>EEE</tag5></tag2>
  <tag3>
    <tag4>CCC</tag4>
  </tag3>
  <tag6>GGG</tag6>
</root>

#1


4  

It's a fairly simple task using a recursive subroutine

这是一个使用递归子例程的相当简单的任务

In the program below, each call to add_xpath advances the value of $node and removes one step from the XPath expression in the $path parameter

在下面的程序中,每个对add_xpath的调用都会提高$node的值,并从$path参数中的XPath表达式中移除一步。

  • If the path begins with a slash and a tag name then the tag name is checked to make sure it matches the name of the root element. Then the current node is set to the root element and the subroutine recurses

    如果路径以斜线和标记名开头,则检查标记名,以确保它与根元素的名称匹配。然后将当前节点设置为根元素,然后将子例程递归

  • If the path starts immediately with a tag name, then has_child is called to see if a child of that name already exists. If not then insert_new_elt adds one for us. The current node is set to the new or pre-existing child node and the subroutine recurses

    如果路径立即以标记名开始,则调用has_child来查看该名称的子节点是否已经存在。如果没有,那么insert_new_elt为我们添加一个。当前节点被设置为新的或已存在的子节点,子例程递归

  • Otherwise the path should be empty, and it is checked to make sure. Then set_text is called to set the text contents of the currenty node and the recursion terminates

    否则路径应该为空,并检查它以确保。然后调用set_text来设置currenty节点的文本内容,递归结束

The output show the resulting XML structure after each of the three operations that you show in your question

输出显示了问题中显示的三个操作中的每个操作之后的结果XML结构

use strict;
use warnings;

use XML::Twig;
use Carp;

my $twig = XML::Twig->new;
$twig->parsefile('test.xml');
$twig->set_pretty_print('indented');
print $twig->sprint, "\n";

add_xpath($twig->root, '/root/tag3/tag4', 'CCC');
print $twig->sprint, "\n";

add_xpath($twig->root, '/root/tag2/tag5', 'EEE');
print $twig->sprint, "\n";

add_xpath($twig->root, '/root/tag6', 'GGG');
print $twig->sprint, "\n";

sub add_xpath {
    my ($node, $path, $value) = @_;

    if ( $path =~ s|^/(\w+)/?|| ) {
        my $tag = $1;
        $node = $node->root;
        carp "Root element has wrong tag name" unless $node->tag eq $tag;
    }
    elsif ( $path =~ s|^(\w+)/?|| ) {
        my $tag = $1;
        if ( my $child = $node->has_child($tag) ) {
            $node = $child;
        }
        else {
            $node = $node->insert_new_elt('last_child', $tag);
        }
    }
    else {
        carp qq{Invalid path at "$path"} if $path =~ /\S/;
        $node->set_text($value);
        return 1;
    }

    add_xpath($node, $path, $value);
}

output

<root>
  <tag1>AAA</tag1>
  <tag2>BBB</tag2>
  <tag3>
    <tag4>DDD</tag4>
  </tag3>
</root>

<root>
  <tag1>AAA</tag1>
  <tag2>BBB</tag2>
  <tag3>
    <tag4>CCC</tag4>
  </tag3>
</root>

<root>
  <tag1>AAA</tag1>
  <tag2>BBB<tag5>EEE</tag5></tag2>
  <tag3>
    <tag4>CCC</tag4>
  </tag3>
</root>

<root>
  <tag1>AAA</tag1>
  <tag2>BBB<tag5>EEE</tag5></tag2>
  <tag3>
    <tag4>CCC</tag4>
  </tag3>
  <tag6>GGG</tag6>
</root>