【问题标题】:XML::Twig change tag or create element based on node existenceXML::Twig 更改标签或基于节点存在创建元素
【发布时间】:2015-11-25 19:26:43
【问题描述】:

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

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

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

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

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

我使用下面的脚本来设置 $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" );

【问题讨论】:

    标签: xml perl perl-module xml-twig


    【解决方案1】:

    使用递归子程序是一项相当简单的任务

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

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

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

    • 否则路径应该为空,并检查以确保。然后调用set_text设置当前节点的文本内容,递归终止

    在您在问题中显示的三个操作中的每一个之后,输出都会显示生成的 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);
    }
    

    输出

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

    【讨论】:

    • 完全正常。我将打印添加到文件以将其保存到文件中。该方法非常好。
    • @dellair:我很乐意提供帮助。我受到this solution 的启发,解决了使用 C# 的类似问题,但XML::Twig 使这变得困难,因为它不会让您访问根节点(根 element 是不同的——它是根 node 的唯一子节点,它没有名称),所以我必须将初始 / 之后的第一步视为特殊情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多