【问题标题】:XML::LibXML replace valueXML::LibXML 替换值
【发布时间】:2025-11-24 14:00:02
【问题描述】:

我有下面的 xml 文件,在这个文件中我想编写一个 perl 脚本,以便我可以 grep 第一本书的名字,然后是作者的名字,如果它们都匹配,则将字段“值”从 false 更改为 true。例如,如果书名是 abc,作者名是 john,则将值从 false 更改为 true。

use warnings;
use strict;
use XML::LibXML;
my $parser = XML::LibXML->new();
my $xmldoc = $parser->parse_file('b.xml');
for my $book ($xmldoc->findnodes('/library/book')) {
    my $name = $book->findvalue('/@name');
    if($name eq "abc")
    {
        print "yes" ;
    }

}

<library>
  <book name="abc" id="3">
    <key name="history">
      <default label="base" value="1"/>
    </key>
    <author name="john">
      <default label="base" value="false"/>
    </author>
  </book>
  <book name="xyz" id="4">
    <key name="science">
      <default label="base" value="1"/>
    </key>
    <author name="nik">
      <default label="base" value="false"/>
    </author>
  </book>

我是 perl 新手,有人可以帮我吗?我已经编写了一些代码来达到检查作者姓名的第一点,但这本身不起作用。

【问题讨论】:

    标签: perl


    【解决方案1】:
    for my $default_author_node ($xmldoc->findnodes(
       '/library/book[@name="abc"]/author[@name="john"]/default'
    )) {
       $default_author_node->setAttribute('value', 'true');
    }
    

    但您可能希望名称是可变的。

    解决方案 1:动态构建上述 XPath。

    sub text_to_xpath {
       my ($s) = @_;
       return qq{"$s"} if $s !~ tr/"//;
       return qq{'$s'} if $s !~ tr/'//;
       $s = s/"/", '"', "/g;
       return qq{concat("$s")};
    }
    
    my $target_book_xp   = text_to_xpath($target_book);
    my $target_author_xp = text_to_xpath($target_author);
    
    for my $default_author_node ($xmldoc->findnodes(
       "/library/book[\@name=$target_book_xp]/author[\@name=$target_author_xp]/default"
    )) {
       $default_author_node->setAttribute('value', 'true');
    }
    

    解决方案 2:自己检查。

    这就是您所尝试的,但是 XPath /@name 在文档的根 (name) 处获取名为 name 的子属性 (/),但唯一的节点是根元素 ( library)。就像在目录路径中一样,如果您想相对于上下文进行搜索,请不要使用前导 /

    for my $book_node ($xmldoc->findnodes('/library/book')) {
       my $name = $book_node->getAttribute('name');
       next if !defined($name) || $name ne $target_book;
    
       for my $author_node ($book_node->findnodes('author')) {
          my $name = $book_node->getAttribute('name');
          next if !defined($name) || $name ne $target_author;
    
          for my $default_author_node ($author_node->findnodes('default')) {
             $default_author_node->setAttribute('value', 'true');
          }
       }
    }
    

    【讨论】:

    • 感谢您的帮助,尝试了解决方案 2,它成功了。我还有一个问题,我想更改同一个 xml 文件中的内容。有没有办法做到这一点?截至目前,我正在使用 print $xmldoc->toString;并将输出重定向到另一个 xml 文件。但我不喜欢这样做。
    • 没有什么能阻止您保存到同一个文件。还有一个名为toFile 的方法可以为您节省一些工作