使用Perl基于其他更新xml属性值

这是我的示例xml文件
<manifest>
 <default>
    <remote>remote1</remote>
    <revision>rev1</revision>
 </default>
 <project>
    <name>common</name>
    <path>opensource/device</path>
    <revision>sa</revision>
    <x-ship>oss</x-ship>
 </project>
 <project>
   <name>external</name>
   <path>source/tp</path>
   <x-ship>none</x-ship>
 </project>
 <project>
   <name>ws</name>
   <path>opensource/ws</path>
   <remote>nj</remote>
   <revision>myno</revision>
   <x-ship>none</x-ship>
 </project>
</manifest>

在此我需要仅在< path>时更新修订值.在其中有“opensource”字符串.

搜索了很多但找不到任何有用的东西来实现这一点,我可以根据下面的位置修改值,有人可以帮我更新吗?或者让我知道是否有更好的Perl库来执行此操作.

#!/usr/bin/perl

use strict;
use warnings;

use XML::Simple;

my $xml_file = 'dev.xml';

my $xml = XMLin(
    $xml_file,KeepRoot => 1,ForceArray => 1,);

$xml->{manifest}->[0]->{project}->[2]->{revision} = 'kyo';

XMLout(
    $xml,NoAttr => 1,OutputFile => $xml_file,);

解决方法

肯定存在学习曲线,但 XML::Twig和XPath语法可以很好地处理这个问题.以下内容演示了您提供的虚假数据的变体.

请注意,twig的一个重要功能是能够随时解析数据,而不必将极大的XML文件完全加载到内存中.这可能不是您的情况下的限制,但对某些人来说这是一个重要的特征.

use strict;
use warnings;

use XML::Twig;

my $data = do { local $/; <DATA> };

my $t= XML::Twig->new( 
    twig_handlers => {
        q{project[string(path) =~ /\bopensource\b/]/revision} => \&revision,},pretty_print => 'indented',);
$t->parse( $data );
$t->print;

sub revision {
    my ($twig,$rev) = @_;
    $rev->set_text("open source - " . $rev->text());
}

__DATA__
<manifest>
 <default>
    <remote>remote1</remote>
    <revision>rev1</revision>
 </default>
 <project>
    <name>common</name>
    <path>NOTopensource/device</path>
    <revision>sa</revision>
    <x-ship>oss</x-ship>
 </project>
 <project>
   <name>external</name>
   <path>source/tp</path>
   <x-ship>none</x-ship>
 </project>
 <project>
   <name>ws</name>
   <path>opensource/ws</path>
   <remote>nj</remote>
   <revision>myno</revision>
   <x-ship>none</x-ship>
 </project>
</manifest>

输出

你会注意到最后一个版本有开源 – 前缀是它.

<manifest>
  <default>
    <remote>remote1</remote>
    <revision>rev1</revision>
  </default>
  <project>
    <name>common</name>
    <path>NOTopensource/device</path>
    <revision>sa</revision>
    <x-ship>oss</x-ship>
  </project>
  <project>
    <name>external</name>
    <path>source/tp</path>
    <x-ship>none</x-ship>
  </project>
  <project>
    <name>ws</name>
    <path>opensource/ws</path>
    <remote>nj</remote>
    <revision>open source - myno</revision>
    <x-ship>none</x-ship>
  </project>
</manifest>

关于兄弟元素的附录:

是的,有一些方法可以在一个树枝内遍历到附近的xml元素.例如,如果我想提取我正在编辑的修订版的名称,并将其放在新文本中,我可以执行以下操作:

sub revision {
    my ($twig,$rev) = @_;
    my $name = $rev->parent()->first_child("name");
    $rev->set_text("open source - " $name->text() . ' - '. $rev->text());
}

请注意,ws现已添加到已编辑的修订版标记中:

<project>
    <name>ws</name>
    <path>opensource/ws</path>
    <remote>nj</remote>
    <revision>open source - ws - myno</revision>
    <x-ship>none</x-ship>
  </project>

这种将树枝遍历到附近元素的方法通常是一种有用的过滤方法.我很容易做到这一点来强制执行这个分支是一个包含opensource的路径,但是如果熟悉xpath语法,那么在xpath中为处理程序设置该需求是很方便的.

还要注意,在上面的例子中,我假设有一个类型名称的兄弟.通常我会在调用之前检查以确保 – > gt; text()或者可能会出错.

关于属性的附录:

关于您的边缘情况与替代格式:

<project path="opensource" revision="apple" name="platform" x-ship="none"/>

上面包含与其他项目相同的数据,但它们不是值为子元素,而是属性.这也是XML的一个特性,但它是不同的,因此必须以不同的方式处理.

以下是对最初建议的脚本的编辑,该脚本为包含路径属性与子项的项目添加新的处理程序:

use strict;
use warnings;

use XML::Twig;

my $data = do { local $/; <DATA> };

my $t= XML::Twig->new( 
    twig_handlers => {
        q{project[string(path) =~ /\bopensource\b/]/revision} => \&revision,q{project[@path =~ /\bopensource\b/]} => \&project,$rev) = @_;
    $rev->set_text("open source - " . $rev->text());
}

sub project {
    my ($twig,$project) = @_;

    $project->set_att(
        revision => 'open source - ' . $project->{att}{revision},);
}

__DATA__
<manifest>
 <default>
    <remote>remote1</remote>
    <revision>rev1</revision>
 </default>
 <project>
    <name>common</name>
    <path>NOTopensource/device</path>
    <revision>sa</revision>
    <x-ship>oss</x-ship>
 </project>
 <project path="opensource" revision="apple" name="platform" x-ship="none"/>
 <project>
   <name>external</name>
   <path>source/tp</path>
   <x-ship>none</x-ship>
 </project>
 <project>
   <name>ws</name>
   <path>opensource/ws</path>
   <remote>nj</remote>
   <revision>myno</revision>
   <x-ship>none</x-ship>
 </project>
</manifest>

只是为了给你一些比较和学习的东西,这里是相同的代码,但在处理程序中使用过滤而不是使用xpath:

twig_handlers => {
        q{project[string(path) =~ /\bopensource\b/]/revision} => \&revision,q{project} => \&project,...

sub project {
    my ($twig,$project) = @_;

    if ($project->{att}{path} && $project->{att}{path} =~ /\bopensource\b/) {
        $project->set_att(
            revision => 'open source - ' . $project->{att}{revision},);
    }
}

相关文章

忍不住在 PerlChina 邮件列表中盘点了一下 Perl 里的 Web 应用框架(巧的是 PerlBuzz 最近也有一篇相关...
bless有两个参数:对象的引用、类的名称。 类的名称是一个字符串,代表了类的类型信息,这是理解bless的...
gb2312转Utf的方法: use Encode; my $str = "中文"; $str_cnsoftware = encode("utf-8...
  perl 计算硬盘利用率, 以%来查看硬盘资源是否存在IO消耗cpu资源情况; 部份代码参考了iostat源码;...
1 简单变量 Perl 的 Hello World 是怎么写的呢?请看下面的程序: #!/usr/bin/perl print "Hello W...
本文介绍Perl的Perl的简单语法,包括基本输入输出、分支循环控制结构、函数、常用系统调用和文件操作,...