我有一个结构如下的XML文档:
<posts>
<user id="1222334">
<post>
<message>hello</message>
<client>client</client>
<time>time</time>
</post>
<post>
<message>hello client how can I help?</message>
<client>operator</client>
<time>time</time>
</post>
</user>
<user id="2333343">
<post>
<message>good morning</message>
<client>client</client>
<time>time</time>
</post>
<post>
<message>good morning how can I help?</message>
<client>operator</client>
<time>time</time>
</post>
</user>
</posts>
我能够创建解析器并打印出整个文档,但是问题是我只想打印(用户)节点和具有特定属性(id)的子节点。
我的PHP代码是:
if( !empty($_GET['id']) ){
$id = $_GET['id'];
$parser=xml_parser_create();
function start($parser,$element_name,$element_attrs)
{
switch($element_name)
{
case "USER": echo "-- User --<br>";
break;
case "CLIENT": echo "Name: ";
break;
case "MESSAGE": echo "Message: ";
break;
case "TIME": echo "Time: ";
break;
case "POST": echo "--Post<br> ";
}
}
function stop($parser,$element_name){ echo "<br>"; }
function char($parser,$data){ echo $data; }
xml_set_element_handler($parser,"start","stop");
xml_set_character_data_handler($parser,"char");
$file = "test.xml";
$fp = fopen($file, "r");
while ($data=fread($fp, filesize($file)))
{
xml_parse($parser,$data,feof($fp)) or
die (sprintf("XML Error: %s at line %d",
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser)));
}
xml_parser_free($parser);
}
在start()
函数中使用此方法可以选择正确的节点,但对读取过程没有任何影响:
if(($element_name == "USER") && $element_attrs["ID"] && ($element_attrs["ID"] == "$id"))
任何帮助都将不胜感激
更新:XMLReader工作,但当使用if语句时,它停止工作:
foreach ($filteredUsers as $user) {
echo "<table border='1'>";
foreach ($user->getChildElements('post') as $index => $post) {
if( $post->getChildElements('client') == "operator" ){
printf("<tr><td class='blue'>%s</td><td class='grey'>%s</td></tr>", $post->getChildElements('message'), $post->getChildElements('time'));
}else{
printf("<tr><td class='green'>%s</td><td class='grey'>%s</td></tr>", $post->getChildElements('message'), $post->getChildElements('time'));
}
}
echo "</table>";
}
正如前面的评论所建议的,您可以选择使用XMLReader
Docs。
XMLReader扩展是XML Pull解析器。阅读器就像一个光标,在文档流中向前移动,并在前进途中的每个节点停下来。
它是一个类(具有相同的名称:XMLReader
),可以打开一个文件。默认情况下,您使用next()
移动到下一个节点。然后检查当前位置是否在一个元素上,然后检查该元素是否有您正在查找的名称,然后您可以处理它,例如通过读取元素XMLReader::readOuterXml()
Docs的外部XML。
与Expat解析器中的回调相比,这有点繁琐。为了获得XMLReader
的更大灵活性,我通常自己创建能够在XMLReader
对象上工作的迭代器,并提供我需要的步骤。
允许直接使用foreach
对具体元素进行迭代。下面是这样一个例子:
require('xmlreader-iterators.php'); // https://gist.github.com/hakre/5147685
$xmlFile = '../data/posts.xml';
$ids = array(3, 8);
$reader = new XMLReader();
$reader->open($xmlFile);
/* @var $users XMLReaderNode[] - iterate over all <user> elements */
$users = new XMLElementIterator($reader, 'user');
/* @var $filteredUsers XMLReaderNode[] - iterate over elements with id="3" or id="8" */
$filteredUsers = new XMLAttributeFilter($users, 'id', $ids);
foreach ($filteredUsers as $user) {
printf("---------------'nUser with ID %d:'n", $user->getAttribute('id'));
echo $user->readOuterXml(), "'n";
}
我已经创建了一个XML文件,其中包含一些更多的帖子,如在您的问题,在id
属性编号从一到上:
$xmlFile = '../data/posts.xml';
然后我创建了一个数组,其中包含两个用户感兴趣的ID值:
$ids = array(3, 8);
将在稍后的过滤器条件中使用。然后创建XMLReader
,并通过它打开XML文件:
$reader = new XMLReader();
$reader->open($xmlFile);
下一步在该读取器的所有<user>
元素上创建迭代器:
$users = new XMLElementIterator($reader, 'user');
然后对先前存储在数组中的id
属性值进行筛选:
$filteredUsers = new XMLAttributeFilter($users, 'id', $ids);
其余部分现在正在迭代foreach
,因为所有条件都已制定:
foreach ($filteredUsers as $user) {
printf("---------------'nUser with ID %d:'n", $user->getAttribute('id'));
echo $user->readOuterXml(), "'n";
}
返回id为3和8的用户的XML:
---------------
User with ID 3:
<user id="3">
<post>
<message>message</message>
<client>client</client>
<time>time</time>
</post>
</user>
---------------
User with ID 8:
<user id="8">
<post>
<message>message 8.1</message>
<client>client</client>
<time>time</time>
</post>
<post>
<message>message 8.2</message>
<client>client</client>
<time>time</time>
</post>
<post>
<message>message 8.3</message>
<client>client</client>
<time>time</time>
</post>
</user>
XMLReaderNode
是XMLReader迭代器的一部分也提供了SimpleXMLElement
Docs,以防您想轻松地读取<user>
元素中的值。
下面的示例展示了如何获取<user>
元素中<post>
元素的计数:
foreach ($filteredUsers as $user) {
printf("---------------'nUser with ID %d:'n", $user->getAttribute('id'));
echo $user->readOuterXml(), "'n";
echo "Number of posts: ", $user->asSimpleXML()->post->count(), "'n";
}
这将显示用户ID 3的Number of posts: 1
和用户ID 8的Number of posts: 3
。
但是,如果外面的XML很长,你就不想这样做了,你想继续在元素内部迭代:
// rewind
$reader->open($xmlFile);
foreach ($filteredUsers as $user) {
printf("---------------'nUser with ID %d:'n", $user->getAttribute('id'));
foreach ($user->getChildElements('post') as $index => $post) {
printf(" * #%d: %s'n", ++$index, $post->getChildElements('message'));
}
echo "Number of posts: ", $index, "'n";
}
生成以下输出:
---------------
User with ID 3:
* #1: message 3
Number of posts: 1
---------------
User with ID 8:
* #1: message 8.1
* #2: message 8.2
* #3: message 8.3
Number of posts: 3
这个例子表明:根据嵌套子元素的大小,您可以使用getChildElements()
提供的迭代器进一步遍历,或者您也可以使用SimpleXML
甚至DOMDocument
等通用XML解析器对XML子集进行遍历。
您可以使用PHP SimpleDomHTML(用PHP5+编写的HTML DOM解析器让您以非常简单的方式操作HTML !)您可以像使用jQuery一样查询数据。它支持HTML,所以肯定也支持XML文档。
您可以在这里下载并查看文档:http://simplehtmldom.sourceforge.net/