【问题标题】:Perl XML::LibXML replace textPerl XML::LibXML 替换文本
【发布时间】:2014-05-19 08:39:06
【问题描述】:

简单的xml文件cr.xml

<book_reviewers>
    <results>
        <reviewer>
            <name>Anne</name>
            <profession>Catfish wrangler</profession>
        </reviewer>
        <reviewer>
            <name>Bob</name>
            <profession>Beer taster</profession>
        </reviewer>
        <reviewer>
            <name>Charlie</name>
            <profession>Gardener</profession>
        </reviewer>
    </results>
</book_reviewers>

我想遍历每个审阅者并将名称替换为新的名称

我并没有尝试同时使用这两种方法,而是通过其他帖子的形式,这两种方法都应该有效,但我不断收到错误:

Can't locate object method "setData"

Can't locate object method "removeChildNodes

Can't locate object method "appendText"

#!/usr/bin/perl -w

use strict;
use XML::LibXML;

my $critics_file = "cr.xml";
my $parser = new XML::LibXML;   


print "Couldn't retrieve review details\n" 
    unless my $book_reviews  = $parser->parse_file($reviews_file);


foreach my $critics ($critic_details->findnodes('/book_reviewers/results/reviewer')) {

    my $value = $critics->findvalue('name');    #returns the correct name
    $value->removeChildNodes();
    $value->appendText('new_name');

     ##ONLY EITHER THE ABOVE METHOD OR THE ONE BELOW - NOT BOTH

    my $node  = $critics->findnodes('.//name.text()');#returns the correct name
    $node->setData('new_name');


}

谁能看出我哪里出错了?

干杯

【问题讨论】:

  • 如果找不到值或节点会怎样???
  • 您的程序无法编译。请显示给出您描述的结果的代码
  • 甚至 XML 都无效。这是一个非常粗心的问题。

标签: xml perl xml-libxml


【解决方案1】:

使用XML::LibXML的解决方案

以下直接访问Text Node即可简单方便地更改附文:

#!/usr/bin/perl
use strict;
use warnings;

use XML::LibXML;

my $doc = XML::LibXML->load_xml( IO => \*DATA );

for my $text ( $doc->findnodes('//reviewer/name/text()') ) {
    $text->setData('new_name');
}

print $doc->toString;

__DATA__
<book_reviewers>
    <results>
        <reviewer>
            <name>Anne</name>
            <profession>Catfish wrangler</profession>
        </reviewer>
        <reviewer>
            <name>Bob</name>
            <profession>Beer taster</profession>
        </reviewer>
        <reviewer>
            <name>Charlie</name>
            <profession>Gardener</profession>
        </reviewer>
    </results>
</book_reviewers>

使用XML::Twig的解决方案

取每个审阅者的名字,然后把他们颠倒过来:

use strict;
use warnings;

use XML::Twig;

my $t= XML::Twig->new( 
    twig_handlers => {
        q{//reviewer/name} => sub { $_->set_text(scalar reverse $_->text()) },
    },
    pretty_print => 'indented',
);
$t->parse( do { local $/; <DATA> } );
$t->print;

__DATA__
<book_reviewers>
    <results>
        <reviewer>
            <name>Anne</name>
            <profession>Catfish wrangler</profession>
        </reviewer>
        <reviewer>
            <name>Bob</name>
            <profession>Beer taster</profession>
        </reviewer>
        <reviewer>
            <name>Charlie</name>
            <profession>Gardener</profession>
        </reviewer>
    </results>
</book_reviewers>

输出:

<book_reviewers>
  <results>
    <reviewer>
      <name>ennA</name>
      <profession>Catfish wrangler</profession>
    </reviewer>
    <reviewer>
      <name>boB</name>
      <profession>Beer taster</profession>
    </reviewer>
    <reviewer>
      <name>eilrahC</name>
      <profession>Gardener</profession>
    </reviewer>
  </results>
</book_reviewers>

【讨论】:

    【解决方案2】:

    问题在于这个调用

    my $value = $critics->findvalue('name')
    

    返回name 元素的字符串内容,您正试图将其视为XML::LibXML::Element 对象。

    只需将方法调用改为

    my ($value) = $critics->findnodes('name')
    

    一切都会好起来的。

    这是您程序的工作版本。

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use XML::LibXML;
    
    my $reviews_file = 'cr.xml';
    my $parser = XML::LibXML->new;
    
    my $doc = $parser->parse_file($reviews_file) or die "Couldn't parse book reviewers\n";
    
    for my $reviewer ($doc->findnodes('/book_reviewers/results/reviewer')) {
      my ($name) = $reviewer->findnodes('name');
      $name->removeChildNodes;
      $name->appendText('new_name');  
    }
    
    print $doc->toString;
    

    输出

    <?xml version="1.0"?>
    <book_reviewers>
        <results>
            <reviewer>
                <name>new_name</name>
                <profession>Catfish wrangler</profession>
            </reviewer>
            <reviewer>
                <name>new_name</name>
                <profession>Beer taster</profession>
            </reviewer>
            <reviewer>
                <name>new_name</name>
                <profession>Gardener</profession>
            </reviewer>
        </results>
    </book_reviewers>
    

    【讨论】:

      【解决方案3】:
      my $value = $critics->findvalue('name');    #returns the correct name
      $value->removeChildNodes();
      $value->appendText('new_name');
      

      $value 是一个简单的标量,它保存找到的 xml 元素 &lt;name&gt;…&lt;/name&gt; 的文本。您不能在“字符串”上调用任何这些方法,可以吗?

      【讨论】:

      • 对不起鲍罗丁-可能只有一个(打勾的答案),但它们都很棒-看到两种方式都更容易理解-我需要去养活我的家人,但我会回来试一试 - 再次感谢
      • @KerynDrake:你一定很无情!我很久以前就放弃了追逐声望点,因为我发现它会干扰写出好的和适当的答案。您应该考虑的主要问题是哪个答案最有可能对遇到类似问题并在搜索中找到您的问题的其他人有用。
      • @KerynDrake 您可能已经注意到一件事,那些对 Perl 充满热情、参与或成为社区成员的人,我们喜欢在遇到麻烦时互相帮助。鲍罗丁也不例外。哦,你可以考虑加入 IRC 和当地的 perl-monger 小组,在那里你可以找到更多对 Perl 感兴趣的人。
      【解决方案4】:

      试图让您进入 TIMTOWTDI,这是另一个代码更紧凑的示例:

      您可以直接找到所有&lt;name&gt;…&lt;/name&gt; 元素,并且可以使用“默认”变量$_ 立即迭代它们。

      use strict;
      use warnings;
      
      use utf8;
      
      use XML::LibXML;
      
      my $filename = "cr.xml";
      
      my $parser = XML::LibXML->new();
      my $critic_details = $parser->parse_file("$filename") or die;
      
      my $new_name = "new_name";
      
      # find ALL the <book_reviewers><results><reviewers><name> nodes
      foreach ($critic_details
        ->findnodes("book_reviewers/results/reviewer/name")
      ) {
        $_->removeChildNodes();
        $_->appendText($new_name);
      }
      
      use XML::LibXML::PrettyPrint;
      my $pretty = XML::LibXML::PrettyPrint->new(
        indent_string =>' ' x4,
        element       => {
          compact       => [qw| name profession |],
          }
        );
      $pretty->pretty_print($critic_details);
      
      print $critic_details->toString;
      
      __END__
      

      享受吧!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多