这是我的示例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()); }
<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},); } }