2005年11月20日

节点匹配路径Xpath

    在利用XSL进行转换的过程中,匹配的概念非常重要。在模板声明语句xsl:template match = ""和模板应用语句xsl:apply-templates select = ""中,用引号括起来的部分必须能够精确地定位节点。具体的定位方法则在XPath中给出。

 

 

   另外,也可以使用Xpath对XML文档进行搜索、定位。

 

 

之所以要引入XPath的概念,目的就是为了在匹配XML文档结构树时能够准确地找到某一个节点元素。可以把XPath比作文件管理路径:通过文件管理路径,可以按照一定的规则查找到所需要的文件;同样,依据XPath所制定的规则,也可以很方便地找到XML结构文档树中的任何一个节点。

 

 

在介绍XPath的匹配规则之前,我们先来看一些有关XPath的基本概念。首先要说的是XPath数据类型。XPath可分为四种数据类型:

 

 

节点集(node-set)
节点集是通过路径匹配返回的符合条件的一组节点的集合。其它类型的数据不能转换为节点集。

 

 

布尔值(boolean)
由函数或布尔表达式返回的条件匹配值,与一般语言中的布尔值相同,有true和false两个值。布尔值可以和数值类型、字符串类型相互转换。

 

 

字符串(string)
字符串即包含一系列字符的集合,XPath中提供了一系列的字符串函数。字符串可与数值类型、布尔值类型的数据相互转换。

 

 

数值(number)
在XPath中数值为浮点数,可以是双精度64位浮点数。另外包括一些数值的特殊描述,如非数值NaN(Not-a-Number)、正无穷大infinity、负无穷大-infinity、正负0等等。number的整数值可以通过函数取得,另外,数值也可以和布尔类型、字符串类型相互转换。

 

 

其中后三种数据类型与其它编程语言中相应的数据类型差不多,只是第一种数据类型是XML文档树的特有产物。另外,由于XPath包含的是对文档结构树的一系列操作,因此搞清楚XPath节点类型也是很必要的。由于XML文档的逻辑结构,一个XML文件可以包含元素、CDATA、注释、处理指令等逻辑要素,其中元素还可以包含属性,并可以利用属性来定义命名空间。相应地,在XPath中,将节点划分为七种节点类型:

 

 

根节点(Root Node)
根节点是一棵树的最上层,根节点是唯一的。树上其它所有元素节点都是它的子节点或后代节点。对根节点的处理机制与其它节点相同。在XSLT中对树的匹配总是先从根节点开始。

 

 

元素节点(Element Nodes)
元素节点对应于文档中的每一个元素,一个元素节点的子节点可以是元素节点、注释节点、处理指令节点和文本节点。可以为元素节点定义一个唯一的标识id。
元素节点都可以有扩展名,它是由两部分组成的:一部分是命名空间URI,另一部分是本地的命名。

 

 

文本节点(Text Nodes)
文本节点包含了一组字符数据,即CDATA中包含的字符。任何一个文本节点都不会有紧邻的兄弟文本节点,而且文本节点没有扩展名。

 

 

属性节点(Attribute Nodes)
每一个元素节点有一个相关联的属性节点集合,元素是每个属性节点的父节点,但属性节点却不是其父元素的子节点。这就是说,通过查找元素的子节点可以匹配出元素的属性节点,但反过来不成立,只是单向的。再有,元素的属性节点没有共享性,也就是说不同的元素节点不共有同一个属性节点。
对缺省属性的处理等同于定义了的属性。如果一个属性是在DTD声明的,但声明为#IMPLIED,而该属性没有在元素中定义,则该元素的属性节点集中不包含该属性。
此外,与属性相对应的属性节点都没有命名空间的声明。命名空间属性对应着另一种类型的节点。

 

 

命名空间节点(Namespace Nodes)
每一个元素节点都有一个相关的命名空间节点集。在XML文档中,命名空间是通过保留属性声明的,因此,在XPath中,该类节点与属性节点极为相似,它们与父元素之间的关系是单向的,并且不具有共享性。

 

 

处理指令节点(Processing Instruction Nodes)
处理指令节点对应于XML文档中的每一条处理指令。它也有扩展名,扩展名的本地命名指向处理对象,而命名空间部分为空。

 

 

注释节点(Comment Nodes)
注释节点对应于文档中的注释。下面,我们来构造一棵XML文档树:

现在,来实现一些利用Xpath使XML中节点匹配的基本方法。

 

 

路径匹配
路径匹配与文件路径的表示相仿,比较好理解。有以下几个符号:

 

 

符  号

 

 

含  义

 

 

举  例

 

 

匹配结果

 

 

/

 

 

指示节点路径

 

 

/A/C/D

 

 

节点"A"的子节点"C"的子节点"D",即id值为d2的D节点

 

 

/

 

 

根节点

 

 

//

 

 

所有路径以"//"后指定的子路径结尾的元素

 

 

//E

 

 

所有E元素,结果是所有三个E元素

 

 

//C/E

 

 

所有父节点为C的E元素,结果是id值为e1和e2的两个E元素

 

 

*

 

 

路径的通配符

 

 

/A/B/C/*

 

 

A元素→B元素→C元素下的所有子元素,即name值为b的B元素、id值为d1的D元素和id值为e1和e2的两个E元素

 

 

/*/*/D

 

 

上面有两级节点的D元素,匹配结果是id值为d2的D元素

 

 

//*

 

 

所有的元素

 

 

|

 

 

逻辑或

 

 

//B | //C

 

 

所有B元素和C元素

 

 

位置匹配
对于每一个元素,它的各个子元素是有序的。如:

 

 

举  例

 

 

含  义

 

 

匹配结果

 

 

/A/B/C[1]

 

 

A元素→B元素→C元素的第一个子元素

 

 

name值为b的B元素

 

 

/A/B/C[last()]

 

 

A元素→B元素→C元素的最后一个子元素

 

 

id值为e2的E元素

 

 

/A/B/C[position()>1]

 

 

A元素→B元素→C元素之下的位置号大于1的元素

 

 

id值为d1的D元素和两个具有id值的E元素

 

 

属性及属性值
在XPath中可以利用属性及属性值来匹配元素,要注意的是,元素的属性名前要有"@"前缀。例如:

 

 

举  例

 

 

含  义

 

 

匹配结果

 

 

//B[@id]

 

 

所有具有属性id的B元素

 

 

id值为b1和b2的两个B元素

 

 

//B[@*]

 

 

所有具有属性的B元素

 

 

两个具有id属性的B元素和一个具有name属性B元素

 

 

//B[not(@*)]

 

 

所有不具有属性的B元素

 

 

A元素→C元素下的B元素

 

 

//B[@id="b1"]

 

 

id值为b1的B元素

 

 

A元素下的B元素

 

 

亲属关系匹配
XML文档可归结为树型结构,因此任何一个节点都不是孤立的。通常我们把节点之间的归属关系归结为一种亲属关系,如父亲、孩子、祖先、后代、兄弟等等。在对元素进行匹配时,同样可以用到这些概念。例如:

 

 

举  例

 

 

含  义

 

 

匹配结果

 

 

//E/parent::*

 

 

所有E节点的父节点元素

 

 

id值为a1的A元素和id值为c1的C元素

 

 

//F/ancestor::*

 

 

所有F元素的祖先节点元素

 

 

id值为a1的A元素和id值为c2的C元素

 

 

/A/child::*

 

 

A的子元素

 

 

id值为b1、b2的B元素,id值为c2的C元素,以及没有任何属性的E元素

 

 

/A/descendant::*

 

 

A的所有后代元素

 

 

除A元素以外的所有其它元素

 

 

//F/self::*

 

 

所有F的自身元素

 

 

F元素本身

 

 

//F/ancestor-or-self::*

 

 

所有F元素及它的祖先节点元素

 

 

F元素、F元素的父节点C元素和A元素

 

 

/A/C/descendant-or-self::*

 

 

所有A元素→C元素及它们的后代元素

 

 

id值为c2的C元素、该元素的子元素B、D、F元素

 

 

/A/C/following-sibling::*

 

 

A元素→C元素的紧邻的后序所有兄弟节点元素

 

 

没有任何属性的E元素

 

 

/A/C/preceding-sibling::*

 

 

A元素→C元素的紧邻的前面所有兄弟节点元素

 

 

id值为b1和b2的两个B元素

 

 

/A/B/C/following::*

 

 

A元素→B元素→C元素的后序的所有元素

 

 

id为b2的B元素、无属性的C元素、无属性的B元素、id为d2的D元素、无属性的F元素、无属性的E元素。

 

 

/A/C/preceding::*

 

 

A元素→C元素的前面的所有元素

 

 

id为b2的B元素、id为e2的E元素、id为e1的E元素、id为d1的D元素、name为b的B元素、id为c1的C元素、id为b1的B元素

 

 

条件匹配
条件匹配就是利用一些函数的运算结果的布尔值来匹配符合条件的节点。常用于条件匹配的函数有四大类:节点函数、字符串函数、数值函数、布尔函数。例如前面提到的last()、position()等等。这些功能函数可以帮助我们精确寻找需要的节点。
  

 

 

函数及功能

 

 

作用

 

 

count()功能

 

 

统计计数,返回符合条件的节点的个数

 

 

number()功能

 

 

将属性的值中的文本转换为数值

 

 

substring() 功能

 

 

语法:substring(value, start, length)


 

 

截取字符串

 

 

sum()功能
  

 

 

求和

 

 

 

 

 

这些功能只是XPath语法中的一部分,还有大量的功能函数没有介绍,而且目前XPath的语法仍然在不断发展中。通过这些函数我们可以实现更加复杂的查询和操作。

 

 
以上这些匹配方法中,用得最多的还要数路径匹配。依靠给出相对于当前路径的子路径来定位节点的。
2005年11月18日

最后的解决方案:用regex 发现数据源字符集,如果PHP自己不能解决就在解析前用mb_convert_encoding将其转换成UTF-8,然后按UTF-8解析,但是解析不了的可能性还是非常高的。我试了一下代码:

$rx = ‘/<?xml.*encoding=['"](.*?)['"].*?>/m’;

if (preg_match($rx, $source, $m)) {
$encoding = strtoupper($m[1]);
} else {
$encoding = "UTF-8";
}

if($encoding == "UTF-8" || $encoding == "US-ASCII” || $encoding == "ISO-8859-1") {
$parser = xml_parser_create($encoding);
} else {

if(function_exists(‘mb_convert_encoding’)) {
$encoded_source = @mb_convert_encoding($source, "UTF-8", $encoding);
}

if($encoded_source != NULL) {
$source = str_replace ( $m[0],’<?xml version="1.0" encoding="utf-8"?>’, $encoded_source);
}

$parser = xml_parser_create("UTF-8");
}

xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");

:) HACK成功,解析ISO-8859-15, BIG-5, 甚至 GB2312 都没有问题,可以将其全部转成UTF-8并在同一个页面中展现。。。我可以宣布这是一个非常艺术的PHP XML字符集声明解析方案,最好能在PHP 4.x中加入。

2005年11月04日

利用PEAR解析RSS文件
发布日期 2005-10-30 浏览次数 6
【字号 大 中 小】【打印】【关闭】

RSS是一种用于共享新闻和其他Web内容的数据交换规范,起源于网景通讯公司的推"Push"技术,将订户订阅的内容传送给他们的通讯协同格式(Protocol)。RSS可以是以下三个解释的其中一个:

Really Simple Syndication(真正简单的整合)
RDF (Resource Description Framework) Site Summary
Rich Site Summary(丰富站点摘要)
但其实这三个解释都是指同一种Syndication的技术。

目前RSS规范的主要版本有0.91、1.0和2.0。

0.91版和1.0版完全不同,风格不同,制定标准的人也不同。0.91版和2.0版一脉相承。1.0版更靠拢XML标准。

现在越来越多的站提供rss,用来共享信息及方便及时阅读,多是在blog,wiki或资讯类网站。当然有一批比较好的阅读器,比如新闻蚂蚁 ,看天下 ,新浪点点通 等等,但还是缺少有些实用的功能,比如会员订阅,会员之间的共享,网上文摘等等。这就需要有一个WEB平台,供RSS在线浏览阅读。

 PHP PEAR就提供一个RSS解析类,方便从用户提供的RSS中,获得相应的信息。

需求
下载XML_RSS: http://pear.php.net/package/XML_RSS
XML_Parser http://pear.php.net/package/XML_Parser
pear http://pear.php.net/package/PEAR


具体路径之间的关系,稍微调试一下即可,我是把压缩包解压后,把里面的RSS.php,Parser.php,PEAR.php都放在同一目录下,然后把RSS.php里面的
require_once ‘XML/Parser.php’;
改成
require_once ‘XML/Parser.php’;
就可以使用了。

RSS版本
经简单的测试,我发现它对RSS上述的三个主要版本,都工作的很好,没有出现问题。说明它是至少支持RSS主要版本的(0.91,1.0,2.0)

例子
用XML_RSS来分析RSS是特别简单的。先包含进类文件:
require "RSS.php";

根据资源地址创建类实例:
$url = "test.rss";
$r =& new XML_RSS($url);

当然这里的$url也可以是一个远程路径,这需要把php.ini里的allow_url_fopen设为On,因为它默认设置即为此,所以一般不用改。

执行分析:
$r->parse();



下面就可以得到各种信息了:
$r->getStructure() 返回是一个数组,包含了RSS文件的所有结构信息。
$r->getChannelInfo() 返回是一个数组,包含了RSS当前频道的概要信息,比如title,link,description等。
$r->getItems() 返回一个二维数组,包含了RSS文件里的所有item信息,每组信息里都有title,link,description,date等等。
$r->getImages() 返回一个二维数组,包含了RSS文件里的所有image信息,每组信息里都有title,link,url等。
$r->getTextinputs() 返回一个二组数组,包含了RSS文件里的所有文件输入区域信息,每组信息里都有title,link,description,name等。

如何根据一个地址打印内容列表

<?php
$url = "http://blog.wangyou.com/rss/index.php";
$r =& new XML_RSS($url);
$r->parse();
$items = $r->getItems()
foreach ($items as $k=>$item)
{
//$item里面包含了 link:内容链接;title:内容标题;description:内容描述;pubdate:内容表布日期,视站点而定,不一定提供
?>
<div style="width:500px"><a href="<?=$item["link"]?>" target="_blank"><?=$item["title"]?></a></div>
<?
}
?>



Parsing XML With PHP

by Marc Robards

Last week, we presented a simple framework (named XMLCast) for distributing content to a variety of devices using XML. This application was built using Microsoft’s Active Server Pages (ASP) technology but we realize that many of you aren’t using ASP (we aren’t either). This article will present the concept of XML parsing using the PHP scripting language. In the coming weeks, we will follow this example up with an expansion of XMLCast using other tools such as XSLT and Cocoon.
Recently, The Wireless Developer Network began offering our daily news in a variety of formats for people that wanted their news delivered in ways other than standard HTML. Among the formats we offer is Rich Site Summary (RSS), an XML format that splits up items (news headlines, in this case) into easily extractable elements, allowing other sites to grab our latest news headlines, format them as they wish, and list them on a page on their site, all with the convinence of XML data exchange.
RSS version 0.91 was developed by Netscape for their "My Netscape Network" and it allows a site to create an XML file that contains basic information about the site, in addition to "items" which can have "title", "link" and "description" nodes. To see an example of our RSS news feed, click here. That’s great, you say, but now that we’ve got the RSS XML document, how do we extract the information and serve it up as HTML? Well, each language has it’s own way to deal with XML, and for this example, we’re using PHP and it’s included XML parser. PHP uses James Clark’s expat library, which you already have if you are using Apache 1.3.9 or later. To parse XML with PHP, you must configure PHP with the –with-xml argument prior to make and make install.
We’ve written a simple PHP script that parses the RSS file, extracts the pertinent information, formats it, and serves it up as regular HTML. Not only does it give an example of how to parse an RSS XML file with PHP, this script can also be added to any PHP file, allowing for automatically updated news headlines straight from our site.

Click here to download the source code.

The first thing we do is create a class to hold our headlines:

class xItem {<br /> var $xTitle;<br /> var $xLink;<br /> var $xDescription;<br /> }<br /> Then, we define a few global variables for the general site information, and an array to hold the headline objects. $sTitle = &quot;&quot;;<br /> $sLink = &quot;&quot;;<br /> $sDescription = &quot;&quot;;<br /> $arItems = array();<br /> $itemCount = 0;<br /> The meat of the XML parsing is in the next three functions, startElement, endElement, and characterData. We’ve used a nice trick by David Medinets from his book PHP3 – Programming Browser-Based Applications for extracting the XML data in PHP. With PHP’s implementation of XML, there’s no easy way to get around using global variables, but David’s way is one of the most straightforward PHP-XML implementations we’ve found. Here’s the first two functions: function startElement($parser, $name, $attrs) {<br /> global $curTag;<br /> $curTag .= &quot;^$name&quot;;<br /> }</p> <p>function endElement($parser, $name) {<br /> global $curTag;<br /> $caret_pos = strrpos($curTag,&#8217;^');<br /> $curTag = substr($curTag,0,$caret_pos);<br /> }<br /> To parse PHP in XML, you define functions to handle:

a) when the parser encounters the start element of a tag
b) when the parser encounters the end element of a tag
c) when the parser encounters the data within the start and end tags

The way we handle these functions is by setting a global variable ($curTag) to a string containg all the parent tags separated by a caret (^). For example, an xml structure that looks like:
&lt;RSS&gt;<br /> &lt;CHANNEL&gt;<br /> &lt;ITEM&gt;<br /> &lt;/ITEM&gt;<br /> &lt;/CHANNEL&gt;<br /> &lt;/RSS&gt;<br /> would translate to a $curTag: ^RSS^CHANNEL^ITEM<br /> when the parser has found the <ITEM> tag. All we have to do is check for when the parser has found the correct $curTag, and extract the data accordingly. That’s all done in the characterData function. Here it is: function characterData($parser, $data) {<br /> global $curTag;<br /> // get the Channel information first</p> <p> global $sTitle, $sLink, $sDescription;<br /> $titleKey = &quot;^RSS^CHANNEL^TITLE&quot;;<br /> $linkKey = &quot;^RSS^CHANNEL^LINK&quot;;<br /> $descKey = &quot;^RSS^CHANNEL^DESCRIPTION&quot;;<br /> if ($curTag == $titleKey) {<br /> $sTitle = $data;<br /> }<br /> elseif ($curTag == $linkKey) {<br /> $sLink = $data;<br /> }<br /> elseif ($curTag == $descKey) {<br /> $sDescription = $data;<br /> }</p> <p> // now get the items<br /> global $arItems, $itemCount;<br /> $itemTitleKey = &quot;^RSS^CHANNEL^ITEM^TITLE&quot;;<br /> $itemLinkKey = &quot;^RSS^CHANNEL^ITEM^LINK&quot;;<br /> $itemDescKey = &quot;^RSS^CHANNEL^ITEM^DESCRIPTION&quot;;<br /> if ($curTag == $itemTitleKey) {<br /> // make new xItem<br /> $arItems[$itemCount] = new xItem();<br /> // set new item object&#8217;s properties<br /> $arItems[$itemCount]-&gt;xTitle = $data;<br /> }<br /> elseif ($curTag == $itemLinkKey) {<br /> $arItems[$itemCount]-&gt;xLink = $data;<br /> }<br /> elseif ($curTag == $itemDescKey) {<br /> $arItems[$itemCount]-&gt;xDescription = $data;<br /> // increment item counter<br /> $itemCount++;<br /> }<br /> }<br /> The characterData function checks if the $curTag is something we want to extract, and if it is, assign it to our variables. The first chunk extracts the general information about the site, and then checks if we’ve come across an <ITEM>. If we have, it creates a new xItem, inserts it into our $arItems array, and sets the properties to the appropriate data from the RSS file.

Now that the functions are defined, we use PHP’s standard way of assigning our functions to the XML parser:
// main loop<br /> $xml_parser = xml_parser_create();<br /> xml_set_element_handler($xml_parser, &quot;startElement&quot;, &quot;endElement&quot;);<br /> xml_set_character_data_handler($xml_parser, &quot;characterData&quot;);<br /> if (!($fp = fopen($uFile,&quot;r&quot;))) {<br /> die (&quot;could not open RSS for input&quot;);<br /> }<br /> while ($data = fread($fp, 4096)) {<br /> if (!xml_parse($xml_parser, $data, feof($fp))) {<br /> die(sprintf(&quot;XML error: %s at line %d&quot;,<br /> xml_error_string(xml_get_error_code($xml_parser)),<br /> xml_get_current_line_number($xml_parser)));<br /> }<br /> }<br /> xml_parser_free($xml_parser);<br /> Everything in the above code that starts with "xml_" in the above code is standard PHP XML functions. We tell PHP’s XML parser we want our functions to execute when the parser comes accross a start tag, end tag, or \ character data, and then we load the RSS file ($uFile, set to our RSS document), and start up the parser (xml_parse).

Now that we have the data in nice little objects and variables, formatting it and serving it up is simple:

&lt;html&gt;<br /> &lt;head&gt;<br /> &lt;title&gt;&lt;/title&gt;<br /> &lt;meta name = &quot;description&quot; content = &quot;&quot;&gt;<br /> &lt;/head&gt;<br /> &lt;body bgcolor = &quot;#FFFFFF&quot;&gt;<br /> &lt;font face = &quot;&quot; size = &quot;&quot;&gt;<br /> &lt;a href = &quot;&quot;&gt;&lt;/a&gt;<br /> &lt;/font&gt;<br /> &lt;br&gt;<br /> &lt;br&gt;<br /> &lt;/body&gt;<br /> &lt;/html&gt;<br /> We’ve added a few user-defined variables to set the font, font size, and whether or not you want the descriptions along with the headlines (see the source code for details), but basically the above code loops through our array of items, echo-ing out them in a basic format.

When it comes to exchanging data, XML is hard to beat. Defining an XML format that can be used by many people (like RSS) is just one of the benefits from using this sophisticated, yet elegant, technology. Parsing XML in PHP may not be quite so straightforward at first, but once you get a handle on it, the possibilities of exchanging data (especially over something like the Internet) are endless.

Suggested Links: