Linux下使用LibXML2处理XML文件
来源:互联网 发布:ubuntu 根目录扩容 编辑:程序博客网 时间:2024/05/10 13:20
引言
要创建完善的、高度互操作性的应用程序,XML 是一个很好的选择,因为它正越来越广泛地应用于数据存储和配置文件管理。本文研究了一个使用 XML(可扩展标记语言)作为其配置文件格式的示例应用程序,并通过该示例向您介绍如何在自己的 UNIX 应用程序中使用 XML。该示例应用程序使用 Perl 编写,并且其中使用了基于 Gnome 项目的 LibXML2 库的 Perl 模块。
在给出 XML 的简单定义之后,本文介绍了一个使用 XML 编写的示例配置文件。然后,通过示例代码来介绍如何解析这个配置文件。系统管理员可以手动修改该配置文件,但通常在一定程度上,需要应用程序直接地修改该配置文件。然后,本文通过一个示例介绍如何以编程的方式向这个 XML 文档添加新的配置选项,以及如何修改当前条目的值。最后,本文介绍了将这个经过修改的配置文件写入到磁盘的代码。
关于 XML在开始研究 LibXML2 库之前,让我们先来巩固一下 XML 的相关基础。XML 是一种基于文本的格式,它可用来创建能够通过各种语言和平台访问的结构化数据。它包括一系列类似 HTML 的标记,并以树型结构来对这些标记进行排列。例如,可参见清单 1中介绍的简单文档。这是配置文件部分中研究的配置文件示例的简化版本。为了更清楚地显示 XML 的一般概念,所以对其进行了简化。
清单 1. 一个简单的 XML 文件 view plaincopy to clipboardprint?
<?xml version="1.0" encoding="UTF-8"?>
<files>
<owner>root</owner>
<act
<age units="days">10</age>
</files>
清单 1中的第一行是 XML 声明,它告诉负责处理 XML 的应用程序,即解析器,将要处理的 XML 的版本。大部分的文件使用版本 1.0 编写,但也有少量的版本 1.1 的文件。它还定义了所使用的编码。大部分文件使用 UTF-8,但是,XML 设计用来集成各种语言中的数据,包括那些不使用英语字母的语言。 接下来出现的是元素。一个元素以开始标记开始(如 ),并以结束标记结束(如 ),其中使用斜线 (/) 来区别于开始标记。元素是 Node的一种类型。XML 文档对象模型 (DOM) 定义了几种不同的 Nodes类型,包括Elements(如 files或者 age)、Attributes(如 units)和 Text(如 root或者 10)。元素可以具有子节点。例如,age 元素有一个子元素,即文本节点 10。而 files 元素有七个子元素。其中三个很明显。它们分别是三个子元素:owner、act
清单 2. 配置文件 view plaincopy to clipboardprint?
<?xml version="1.0" encoding="UTF-8"?>
<filesystem>
<path>
<dirname>/var</dirname>
<files>
<owner>root</owner>
<act
<age units="days">10</age>
</files>
<files>
<owner>any</owner>
<act
<age units="hours">96</age>
</files>
</path>
<path>
<dirname>/tmp</dirname>
<files>
<owner>any</owner>
<act
<age units="hours">24</age>
</files>
</path>
</filesystem>
在本例中,根元素是 filesystem,它包含两个 path元素。每个 path元素包含相应的目录名和一个或多个 files元素。每个 files元素通过 age元素的 units属性中指定的存在时间单位,定义了当用户或用户文件达到特定的存在时间时,应用程序应采取的操作。请记住,空白符号是有意义的。从结构的观点来看,每个空白符号组成了独立的 Text节点。在产品环境中,一个编写完善的 UNIX 应用程序不仅应该具有读取数据并对其进行操作的能力,而且还应具有根据用户输入对数据进行添加、删除和修改的能力。
现在让我们来研究使用该数据的应用程序。示例程序本文余下的内容通过示例代码介绍对 XML 配置文件的解析和管理。这些示例逐一地读取并修改配置文件,但在 UNIX 开发人员的日常工作中,您可以在任何类型的任务中使用这些概念。而且,因为使用了 LibXML2 库,所以您可以将这些概念插入到几乎任何的 UNIX 应用程序中。我们将在本文中介绍使用 Perl 版本的 LibXML2 库的示例。Internet 上的大部分文档都在讨论如何使用 Java 或 Microsoft Visual Studio 工具进行编程,但对于 UNIX 用户或开发人员来说,Perl 则更有价值。清单 3显示了解析该 XML 文档所需的 Perl 模块。清单 3. 需要的库
XML::LibXML
XML::LibXML::Common
XML::NamespaceSupport
XML::SAX
下面部分中介绍的代码仅仅只是一个框架。可分三个部分对其进行介绍:解析、操作和导出。
在加载和解析阶段中,可能会将数据加载到 Perl 变量中,如列表或哈希,但由于每个程序员都有他/她自己首选的方法来完成这项任务,所以我们把它留给读者作为一项练习。下面的代码只是简单地显示了数据,以此说明该脚本正确地找到了相应的数据。
在操作阶段中,该程序对 XML 文档中的元素进行添加、修改和删除的数据更新操作。通常地,这将按照用户的操作来进行。 最后在导出阶段中,将经过修改的最终的文档写回到磁盘。
加载和解析数据对于应用程序来说,读取 XML 文件的第一步是加载该数据并将其解析为一个 Document对象。在此基础上,可以对 DOM 树进行遍历以获取特定的节点。让我们来看看清单 4中的代码是如何完成该任务的。
清单 4. 加载和解析 example.xml 的代码
my $parser = XML::LibXML->new();
my $doc = $parser->parse_file("example.xml");
$filesystem = $doc->getDocumentElement();
@nodes=$filesystem->childNodes;
foreach $node (@nodes) {
if($node->nodeType==ELEMENT_NODE) {
# ignore text nodes
# just get the first match
@dirnames = $node->getElementsByTagName("dirname")->item(0);
foreach $dirname (@dirnames) {
print "dirname: ". $dirname->textContent . "\n"; # push this into an array
}
# get all children
@files = $node->getChildrenByTagName("files");
foreach $file (@files) {
foreach $values ($file->childNodes) {
# ignore text nodes
if($values->nodeType!=XML_TEXT_NODE) {
if($values->nodeName() eq "age") {
# check for attribute, otherwise, use default of 'hours'
if($values->hasAttributes()) {
print $values->nodeName() . ": ". $values->textContent;
print "". $values->attributes->item(0)->value();
print "\n";
}
else {
print $values->nodeName() . ": ". $values->textContent;
print "hours\n";
}
# calculate extended value from units and put in a
# hash linked with this dirname, etc.
}
else {
print $values->nodeName() . ": ". $values->textContent;
print "\n";
# put this value into a hash linked with $dirname.
# We may have multiple entries for each $dirname, so
# perhaps use an array within a hash
}
}
}
}
}
}
首先,在清单 4中,创建了解析器并将 XML 从文件加载到 XML::LibXML::Document变量。这个对象包含了整个 XML 树,并且具有与之关联的各种方法可用来搜索节点、导出、验证和创建新的节点。本文将在后面的几个部分中对其中的一些方法进行介绍。从代码的起始处开始,您可以看到 getDocumentElement()方法,它用于返回文档的根节点。从这个根节点,就可以遍历整个 XML 树。 主foreach循环对父 filesystem元素中的每个节点进行循环。当仅选择元素节点时,该循环将得到 path 元素。getElementsByTagName()方法根据相应的名称在节点中搜索对应的元素,并通过 NodeList对象返回它们。每个 path元素包含了一个 dirname元素,所以代码搜索名称为 dirname的元素,并获取其中的第一个条目。在代码中只能选择 ELEMENT 类型的节点,因为该方法不支持 TEXT 节点,并且会在 Perl 中产生一个不可恢复的错误。在一个 path 元素中可能存在多个 files元素,所以代码对 getChildrenByTagName()方法的每一个元素进行循环,这个方法类似于 getElementsByTagName(),但仅搜索目标节点的直接子节点。这将返回所有的 files元素,但必须进行进一步的解析以获得 owner、act
print $values->nodeName() . ": "
print $values->firstChild()->nodeValue();
在使用 age元素的情况下,还可以通过一个属性来指定时间单位。使用 hasAttributes()和 Attributes函数,该程序可以提取相应的属性,如果它存在话。如果它不存在,那么该程序使用缺省值 hours。
现在让我们来介绍对数据的操作,这样就能够以编程的方式来添加、删除和编辑操作。对数据进行操作现有的代码本身就是一个有用的程序。用户可以很容易地以手动地方式对该 XML 文件完成程序所做的修改。然而,有经验的 UNIX 开发人员还可以使用 XML 函数在程序中直接对文件进行修改。例如,可以包含用于添加新的操作或删除现有操作的菜单选项。要实现这个目的,让我们来看看在程序中对数据进行操作的代码。
清单 5. 向文档添加填充的 path 节点
$newnode = $doc->createElement("path");$newdirnode = $doc->createElement("dirname");
$newdirnode->appendText("/root");
$newfilesnode = $doc->createElement("files");
$newownernode = $doc->createElement("owner");
$newownernode->appendText("any");
$newactionnode = $doc->createElement("action");
$newactionnode->appendText("archive");
$newagenode = $doc->createElement("age");
$newagenode->appendText("30");
$newagenode->setAttribute("units","days");
$newfilesnode->addChild($newownernode);
$newfilesnode->addChild($newactionnode);
$newfilesnode->addChild($newagenode);
$newnode->addChild($newdirnode);
$newnode->addChild($newfilesnode);
$filesystem->addChild($newnode);
清单 5中的代码创建了一个 path元素,并为其填充了所有的元素。然后,将这个新创建的节点添加到根元素,即 filesystem。需要使用 XML::LibXML::Document类的 createElement()方法来创建每个元素。(正是 Document创建了您所需要的任何新的节点。)该方法返回一个尚未连接到文档树中任何位置的空节点。然后可以使用 XML::LibXML::Element 类的 appendText()方法为每个节点添加内容。此外,相对于创建一个新的 TEXT 节点,然后对其进行填充并将其添加到相应的元素,这是一种快捷的方法。可以使用 setAttribute()方法来添加属性,如果在目标元素中不存在给定名称的属性,该方法将自动创建一个新的 ATTRIBUTE节点。在完成每个节点的创建并分别对它们进行了填充之后,可以使用要求的子节点作为参数,对父节点调用 addChild()方法。因此在上面的代码中,$newownernode成为了 $newfilesnode的子节点。文档中所有的节点保持其添加时的顺序。如果您希望指定其他的顺序,可以使用 insertAfter()或 insertBefore()函数。将每个节点添加到相应的父节点,直到最后将主父节点添加到已经存在于文档中的一个节点。在上面的示例中,将该节点添加到了 filesystem根节点。(如果您是从头开始创建该文档,那么可以对 Document对象本身调用 addChild()来添加根元素,然后再向该元素添加任何其他的节点。)正如前面所解释的,清单 2中的示例 XML 代码是一种可读的格式。换行和缩进使得文档更容易阅读。XML 解析器将读取所有这些字符,并将其作为一个 TEXT 类型的节点。清单 5中的示例没有添加任何这样的 TEXT 节点。因此,该示例的输出将不包含任何换行或缩进。如果您希望创建这种空白字符,那么需要使用 XML::LibXML::Text类来创建 TEXT 类型的节点,或者使用该文档对象的 createTextNode()函数。该构造函数的返回值是一个节点,可以使用与上面示例中相同的方式将其添加到树中。 要更改文件的内容,可以直接设置相关 TEXT 节点的 nodeValue(),或者替换整个元素:
$newnode = $doc->createElement("owner");
$newnode->appendText("toor");
$oldnode->replaceNode($newnode);
要删除一个节点,有以下几种选择。一种方法是仅将其从结构中删除,代码如下所示:
$file->unbindNode();
在找到需要删除的节点之后,通过一行命令即可将其从结构中删除,但并没有从文档中删除。这个函数调用直到程序结束时才真正销毁该数据结构。如果您需要将节点移动到树中的其他部分,那么可以使用相同的变量来调用 addNode()以将其重新添加到文档中新的位置。您还可以使用 removeChild()或 removeChildNodes()函数,这样可以从文档中彻底地释放相应的资源。保存 XML 文件在一些编程语言中,将 XML 文档保存到文件中可能比较烦琐,但幸运的是,LibXML 让这项任务变得非常简单:
$doc->toFile("example.xml");
在对数据进行的所有的操作中,这是最简单的一种操作。在对内存中的 XML 文档完成了相应的修改之后,只需使用一个函数调用就可以将其写回到对应的配置文件中。还可以使用相关的 Perl 函数,如 toString()和 toFH(),这些函数分别将 XML 输出到一个字符串变量或者一个已打开的 Perl 文件句柄,而文件句柄将为您的应用程序的构建带来更大的灵活性。
结束语
通过提供 LibXML2 库以及对 Perl 模块的支持,Gnome 项目完成了一项很有价值的任务。本文对管理和使用 XML 配置文件所需要的三个重要的步骤进行了介绍。解析阶段可能是最复杂的,因为它需要一定程度的递归设计来解析 XML 树。尽管有些烦琐,但对内存中 XML 文档的操作却是非常简单明了的。使用 LibXML2 库导出经过修改的配置,也是非常容易的。 尽管相对于标准的UNIX 思维方式来说,需要进行一定的思维模式转移,但是 XML 可以为数据管理提供一种功能强大的方法。与简单的数据库格式相比,树型结构提供了更加灵活的数据视图。在开发新的应用程序或修改旧的应用程序时,可将其规范化为使用 XML 配置文件,在进行规范化的过程中可以很容易地使用 Gnome 项目所提供的免费的标准库,正如本文所介绍的。 本文转自http://www.chinaitpower.com/2006Aug/2006-08-25/212810.html
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/alex_yan/archive/2008/07/07/2622296.aspx
- Linux下使用LibXML2处理XML文件
- Linux下使用LibXML2处理XML文件
- linux下,纯c++使用libxml2读取xml文件
- linux下,纯c++使用libxml2读取xml文件
- linux下使用libxml2库,解析xml文件
- windows下使用libxml2处理XML报文
- window下使用libxml2读取xml文件
- Linux下使用libxml2解析XML配置文件
- [libxml2]_[XML处理]_[使用libxml2的xpath特性修改xml文件内容]
- linux下使用libxml2实现对xml文件的读取及查询
- linux下,eclipse开发环境,使用libxml2解析xml出现找不到头文件的解决办法
- Linux下使用libxml2
- linux下libxml2使用
- linux 下Libxml2使用
- linux下 libxml2 xml解析
- Ubuntu下C语言使用libxml2库解析xml文件
- Linux环境下C使用的XML解析库:libxml2
- Linux环境下C使用的XML解析库:libxml2
- poj 2184 01背包变形:体积为负数的处理
- Bribing FIPA(树形DP)输入难,学会stringstream的用法,map的使用
- x server实现技术分析
- document、location、body 属性方法
- PHP里10个鲜为人知但却非常有用的函数
- Linux下使用LibXML2处理XML文件
- 虚函数和纯虚函数的区别
- JS操作cookie
- LESSCSS是CSS的扩展
- Android-4.0.4 添加触摸屏按键处理及java中使用重定位命令
- C变量的声明和定义
- presentation and techniques for 3d object recognition and scene interpretation一书阅读笔记(更新中)
- JS如何判断包括IE11在内的IE浏览器
- free命令详解