Linux下使用LibXML2处理XML文件

   引言   要创建完善的、高度互操作性的应用程序,XML 是一个很好的选择,因为它正越来越广泛地应用于数据存储和配置文件管理。本文研究了一个使用 XML(可扩展标记语言)作为其配置文件格式的示例应用程序,并通过该示例向您介绍如何在自己的 UNIX 应用程序中使用 XML。该示例应用程序使用 Perl 编写,并且其中使用了基于 Gnome 项目的 LibXML2 库的 Perl 模块。   在给出 XML 的简单定义之后,本文介绍了一个使用 XML 编写的示例配置文件。然后,通过示例代码来介绍如何解析这个配置文件。系统管理员可以手动修改该配置文件,但通常在一定程度上,需要应用程序直接地修改该配置文件。然后,本文通过一个示例介绍如何以编程的方式向这个 XML 文档添加新的配置选项,以及如何修改当前条目的值。最后,本文介绍了将这个经过修改的配置文件写入到磁盘的代码。    关于 XML   在开始研究 LibXML2 库之前,让我们先来巩固一下 XML 的相关基础。XML 是一种基于文本的格式,它可用来创建能够通过各种语言和平台访问的结构化数据。它包括一系列类似 HTML 的标记,并以树型结构来对这些标记进行排列。   例如,可参见清单 1中介绍的简单文档。这是配置文件部分中研究的配置文件示例的简化版本。为了更清楚地显示 XML 的一般概念,所以对其进行了简化。    清单 1. 一个简单的 XML 文件
<?xml version="1.0" encoding="UTF-8"?>

            <files>

            <owner>root</owner>

            <action>delete</action>

            <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、action和 age。其他四个分别是元素前后的空白文本符号。  XML 解析器可以利用这种父子结构来遍历文档,甚至修改文档的结构或内容。LibXML2 是这样的解析器中的其中一种,并且文中的示例应用程序正是使用这种结构来实现该目的。对于各种不同的环境,有许多不同的解析器和库。LibXML2 是用于 UNIX 环境的解析器和库中最好的一种,并且经过扩展,它提供了对几种脚本语言的支持,如 Perl 和 Python。   作为研究工作的第一步,让我们先来看一个示例配置文件。    配置文件   文中的示例应用程序读取了特定文件的操作列表。而配置文件定义了这些文件和操作。假设该配置文件是位于 UNIX 文件系统中某个位置的一个文件。例如,在 UNIX 系统 cron 中可能会使用这个配置文件。该 XML 文件定义了目录路径和根据条件要执行的操作,而这些条件则包括所有者和文件存在时间(请参见清单 2)。    清单 2. 配置文件
<?xml version="1.0" encoding="UTF-8"?>

            <filesystem>

            <path>

            <dirname>/var</dirname>

            <files>

            <owner>root</owner>

            <action>delete</action>

            <age units="days">10</age>

            </files>

            <files>

            <owner>any</owner>

            <action>delete</action>

            <age units="hours">96</age>

            </files>

            </path>

            <path>

            <dirname>/tmp</dirname>

            <files>

            <owner>any</owner>

            <action>delete</action>

            <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、action和 age元素。在得到了这些节点之后,可以调用 textContent以从相应的元素中获取实际的值。下面显示的是选择 ELEMENT 节点孩子的 TEXT 节点值的快捷方法:   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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值