CPAN上的XML模块大概可以分三类:对XML数据提供独特接口的模块(通常有关在XML实例和Perl数据之间的转换),实现某一标准XML API的模块,和对一些特定的XML相关任务进行简化等特殊用途的模块。其中,第一种模块也可以理解为自定义标准来解析XML,接下来要讲述的就是这一种类的其中一个模块。在对比了该种类多个模块后,我决定使用XML::Twig,原因是它功能比较强大,而且简单易用,对比XML::Simple要强,既可以输入也可以输出。
虽然XML的用途非常多,但大部分的任务可以分两组:一、从已有的XML文档中提取数据,二使用其他资源的数据创建一个新的XML文档。
一、简介
如果您想寻找一种快速、节省内存资源的方式来处理XML文档,但使用SAX接口又觉得太复杂的话,XML::Twig是其中一种解决方案。XML::Twig模块比较类似DOM接口,在其中可以找到很多类似的DOMish特征,就像一个使用XPath节点语法来实现的小型SAX处理过程。
我们同样可以参考DOM的语法,把XML文档中的内容看成树目录结构,底下是各个节点,节点也分开元素节点、属性节点和文本节点几种类型。例如:
/ \
head body
/ \ \
script title div
/ \
h1 p
在这里head、 body节点就是html的分支(或称twig 小枝)。XML::Twig通过使用XPath的语法来具体指定某一节点位置,然后对其进行操作。
二、简单操作
1、示例XML文件
这里,为了说明具体的概念,我使用博客上提供的RSS XML文件来做源文件。通过对该文件的解析来输入和处理、输出XML。
获取该文件的方式很简单,点击首页右下角的RSS:日志。
下载后,把该文件保存为feed.xml文件。
其大概的结构如下:
<rss version="2.0">
<channel>
<title>博客网站名称</title>
<link>博客网站链接</link>
<description>简要描述</description>
<language>zh-cn</language>
<copyright>版权信息</copyright>
<item>
<link>每个日志的具体链接</link>
<title>每个日志的标题</title>
<author>作者信息</author>
<category>分类信息</category>
<pubDate>发布时间</pubDate>
<guid>一个唯一链接标记</guid>
<description>每个日志的简要描述</description>
</item>
<item>
……
</item>
</channel>
</rss>
2、定义TwigRoots
当使用一个XML文件创建XML::Twig实例的时候,TwigRoots就用于接受单个has的引用,该值使用XPath语法结构来表示节点在输入XML文档中的具体问题。如果定义多个TwgRoots,则只有所有定义的节点的根节点作为最后的TwigRoots的值。
举例,把TwigRoots定义在title节点的问题,则从该节点开始开始处理:
use encoding "utf-8";
use XML::Twig;
my $file = "./feed.xml";
# 定义一个XML::Twig实例
my $twig = new XML::Twig(TwigRoots => {'title' => 1},pretty_print => 'indented');
# 打开指定的XML文件
$twig->parsefile($file);
$twig->print;
print "\n";
结果:
(如果没有pretty_print => 'indented',则不会分行输出)
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<title><![CDATA[linuxの飘扬]]></title>
<title><![CDATA[[转]heartbeat CRM管理程序功能示例]]></title>
<title><![CDATA[heartbeat 2.x简单配置[2]]]></title>
<title><![CDATA[heartbeat 2.x简单配置[1]]]></title>
<title><![CDATA[heartbeat 2.x基础概念]]></title>
<title><![CDATA[[转]关于脑中风急救–放血救命法]]></title>
<title><![CDATA[清明节的起源和习俗]]></title>
<title><![CDATA[关闭Apache的目录浏览功能]]></title>
<title><![CDATA[[转]用好sudo]]></title>
<title><![CDATA[发布ExtMail 1.0.8/ Extman 1.0.0安装及升级rpm ]]></title>
<title><![CDATA[流量监控脚本 v1.3]]></title>
</rss>
结果输出每个title节点的内容。
当定义TwigRoots的时候,可以使用Xpath语法来指定具体的问题,处理的内容也包括该节点后的所有子节点。
例如把脚本该为下面的内容:
my $twig = new XML::Twig(TwigRoots => {'item/title' => 1});
第一个会输出item节点下的所有内容(包括子节点信息),第二个把TwigRoots定义在<item><title>的位置,输出就不包括博客网站标题的title信息了。
3、定义TwigHandlers
上面的输出中并没有对捕捉到的XML内容进行处理,仅是简单的完全输出,而TwigHandlers则允许我们对这些内容进行加工。TwigHandlers接受一个函数的引用,对特定的节点进行修改,而不满足条件的节点不做处理,保留输出。(除非你强制删掉不符合要求的节点内容)
use XML::Twig;
# 定义对item/title节点的处理函数为&item_title,接受的是函数引用
my $twig_handlers = {'item/title' => \&item_title};
my $file = "./feed.xml";
# 创建实例时,增加TwigHandlers属性的定义
my $twig = new XML::Twig(TwigRoots => {'title' => 1},
TwigHandlers => $twig_handlers);
$twig->parsefile($file);
$twig->print;
print "\n";
# 对item/title节点的处理函数
sub item_title {
my ($twig,$title) = @_;
my $title_text = $title -> text;
$title_text =~ s/<![CDATA[(.*)]>/$1/;
# 设置其的文本节点内容
$title -> set_text($title_text);
# 设置其的属性节点内容
$title -> set_att('type','blog');
}
结果:
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<title><![CDATA[linuxの飘扬]]></title>
<title type="blog">[转]heartbeat CRM管理程序功能示例</title>
<title type="blog">heartbeat 2.x简单配置[2]</title>
<title type="blog">heartbeat 2.x简单配置[1]</title>
<title type="blog">heartbeat 2.x基础概念</title>
<title type="blog">[转]关于脑中风急救–放血救命法</title>
<title type="blog">清明节的起源和习俗</title>
<title type="blog">关闭Apache的目录浏览功能</title>
<title type="blog">[转]用好sudo</title>
<title type="blog">发布ExtMail 1.0.8/ Extman 1.0.0安装及升级rpm </title>
<title type="blog">流量监控脚本 v1.3</title>
</rss>
请留意,item_title函数对不属于item/title的博客网站标题节点并没有处理。
◎ 如果你需要定义多个TwigHandlers,可以这样做:
a、格式一
'item2/title' => \&item2_title};
my $twig = new XML::Twig(TwigRoots => {'title' => 1},
TwigHandlers => $twig_handlers);
b、格式二
TwigHandlers => {'item/title' => \&item_title,
'item2/title' => \&item2_title});
4、描述子节点
与DOM相同,除了可以过滤节点内容,XML::Twig模块还支持子节点的定义。
◎ 示例1:
#!/bin/perl -w
use XML::Twig;
# 定义对item节点的处理函数为&item函数
my $twig_handlers = {'item' => \&item};
my $file = "./feed.xml";
# 创建实例时,增加TwigHandlers属性的定义
my $twig = new XML::Twig(TwigRoots => {'item' => 1},
TwigHandlers => $twig_handlers);
# 打开指定的XML文件
$twig->parsefile($file);
#不要输出整个树节点
#$twig->print;
print "\n";
# 对item节点的处理函数
sub item {
my ($twig,$item) = @_;
# 获得item节点下的第一个子节点,并且tag是title的,如果不指定tag,则获得全部第一个子节点
my $title = $item -> first_child('title');
# 同样的,这次获得的是title节点的文本节点值
my $title_text = $item -> first_child_text('title');
$title_text =~ s/<![CDATA[(.*)]>/$1/;
$title -> set_text($title_text);
$title -> set_att('type','blog');
# 输出
$title -> print;
}
结果:
<title type="blog">[转]heartbeat CRM管理程序功能示例</title>
<title type="blog">heartbeat 2.x简单配置[2]</title>
<title type="blog">heartbeat 2.x简单配置[1]</title>
<title type="blog">heartbeat 2.x基础概念</title>
<title type="blog">[转]关于脑中风急救–放血救命法</title>
<title type="blog">清明节的起源和习俗</title>
<title type="blog">关闭Apache的目录浏览功能</title>
<title type="blog">[转]用好sudo</title>
<title type="blog">发布ExtMail 1.0.8/ Extman 1.0.0安装及升级rpm </title>
<title type="blog">流量监控脚本 v1.3</title>
◎ 示例2:
使用循环来对子节点进行遍历,只修改tag为title的节点并输出,其他不输出:
use XML::Twig;
use encoding "utf-8";
# 定义对item/title节点的处理函数为&item_title函数
my $twig_handlers = {'item' => \&item};
my $file = "./feed.xml";
# 创建实例时,增加TwigHandlers属性的定义B
my $twig = new XML::Twig(TwigRoots => {'item' => 1},
TwigHandlers => $twig_handlers,
pretty_print => 'indented');
$twig->parsefile($file);
# 打开指定的XML文件
$twig->print;
print "\n";
# 对item/title节点的处理函数A
sub item {
my ($twig,$item) = @_;
# 对子节点进行遍历,需要使用children方法获得所有的子节点(数组)
# 但不能使用first_child,因为其不带参数时,返回的是第一个子节点对象
for my $child ($item -> children) {
# 获得该节点的tag名称,可以使用gi、name、tag方法,是同义的
my $child_name = $child -> name;
if ( $child_name eq 'title' ) {
my $child_text = $child -> text;
$child_text =~ s/<![CDATA[(.*)]>/$1/;
$child -> set_text($child_text);
$child -> set_att('type','blog');
}
else {
$child -> delete;
}
}
}
输出结果:
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<item>
<title type="blog">[转]heartbeat CRM管理程序功能示例</title>
</item>
<item>
<title type="blog">heartbeat 2.x简单配置[2]</title>
</item>
<item>
<title type="blog">heartbeat 2.x简单配置[1]</title>
</item>
<item>
<title type="blog">heartbeat 2.x基础概念</title>
</item>
<item>
<title type="blog">[转]关于脑中风急救–放血救命法</title>
</item>
<item>
<title type="blog">清明节的起源和习俗</title>
</item>
<item>
<title type="blog">关闭Apache的目录浏览功能</title>
</item>
<item>
<title type="blog">[转]用好sudo</title>
</item>
<item>
<title type="blog">发布ExtMail 1.0.8/ Extman 1.0.0安装及升级rpm </title>
</item>
<item>
<title type="blog">流量监控脚本 v1.3</title>
</item>
</rss>
可以对比一下两个示例输出信息的差异。
5、使用XML::Twig::Elt方法创建元素节点
与上面的类似,XML:Twig也可以根据需要创建元素节点,并给其赋予属性节点和文本节点:
#!/bin/perl -w
use XML::Twig;
# 定义对item节点的处理函数为&item函数
my $twig_handlers = {'item' => \&item};
my $file = "./feed.xml";
# 创建实例时,增加TwigHandlers属性的定义
my $twig = new XML::Twig(TwigRoots => {'item' => 1},
TwigHandlers => $twig_handlers);
# 打开指定的XML文件
$twig->parsefile($file);
#不要输出整个树节点
#$twig->print;
print "\n";
# 对item节点的处理函数
sub item {
my ($twig,$item) = @_;
# 获得item节点下的第一个子节点,并且tag是title的,如果不指定tag,则获得全部第一个子节点
my $title = $item -> first_child('title');
# 同样的,这次获得的是title节点的文本节点值
my $title_text = $item -> first_child_text('title');
my $link_text = $item -> first_child_text('link');
$title_text =~ s/<![CDATA[(.*)]>/$1/;
$title -> set_text('');
# 创建一个新a节点
my $newlink = XML::Twig::Elt -> new('a');
# 赋予属性节点
$newlink -> set_att('href',$link_text);
# 赋予文本节点
$newlink -> set_text($title_text);
$newlink -> paste('first_child',$title);
# 输出
$title -> print;
}
结果:
<title><a href="http://www.linuxfly.org/post/365/">[转]heartbeat CRM管理程序功能示例</a></title>
<title><a href="http://www.linuxfly.org/post/364/">heartbeat 2.x简单配置[2]</a></title>
<title><a href="http://www.linuxfly.org/post/363/">heartbeat 2.x简单配置[1]</a></title>
<title><a href="http://www.linuxfly.org/post/362/">heartbeat 2.x基础概念</a></title>
<title><a href="http://www.linuxfly.org/post/361/">[转]关于脑中风急救–放血救命法</a></title>
<title><a href="http://www.linuxfly.org/post/360/">清明节的起源和习俗</a></title>
<title><a href="http://www.linuxfly.org/post/354/">关闭Apache的目录浏览功能</a></title>
<title><a href="http://www.linuxfly.org/post/353/">[转]用好sudo</a></title>
<title><a href="http://www.linuxfly.org/post/352/">发布ExtMail 1.0.8/ Extman 1.0.0安装及升级rpm </a></title>
<title><a href="http://www.linuxfly.org/post/347/">流量监控脚本 v1.3</a></title>
这里介绍的仅是基本的使用说明,更多的模块可用属性和方法请看perldoc XML::Twig,或CPAN上的介绍。
本次示例下载:
三、参考资料
快速开始Perl XML:接口篇
Using XML::Twig
CPAN 上的XML::Twig模块说明文档
一些更具体的示例
四、补充说明
1、处理输出格式问题
原输出时,所有XML文件为单行,加入pretty_print => 'indented'可优化格式输出结果:
2、解决源代码运行时的一个警告
信息为:
在代码中加入:
问题解决,参考为:点击
3、去掉CDATA信息
上面的代码是用于举例,实际中,若不希望捕获的信息中包含CDATA信息,可以在创建实例时,给remove_cdata一个真值,例如:
pretty_print => 'indented',
remove_cdata => 'true');
则结果为:
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<title>[转]heartbeat CRM管理程序功能示例</title>
<title>heartbeat 2.x简单配置[2]</title>
<title>heartbeat 2.x简单配置[1]</title>
<title>heartbeat 2.x基础概念</title>
<title>[转]关于脑中风急救–放血救命法</title>
<title>清明节的起源和习俗</title>
<title>关闭Apache的目录浏览功能</title>
<title>[转]用好sudo</title>
<title>发布ExtMail 1.0.8/ Extman 1.0.0安装及升级rpm </title>
<title>流量监控脚本 v1.3</title>
</rss>
同理,XML::Twig模块也提供了创建和删除CDATA等标签的方案。
创建:
结果:
删除:
(以代替上面的正规表达式为例)
my ($twig,$title) = @_;
$title -> set_remove_cdata ('#CDATA');
# 设置其的属性节点内容
$title -> set_att('type','blog');
}
关于CDATA的解析,见:这里。
[转]XML Schema 与 XML DTD的技术比较与分析
用Schema规范XML文档
用DTD规范XML文档
XML文档规则
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/110899.html