4

我有一个具有以下结构的 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()函数中使用 this 可以选择正确的节点,但对读取过程没有任何影响:

    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>";
}
4

2 回答 2

8

正如前面评论中所建议的,您也可以使用XMLReaderDocs

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属性中从 1 开始编号:

$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>

作为XMLReader 迭代器XMLReaderNode的一部分的which还提供了一个Docs,以防您想轻松地读取元素内部的值。SimpleXMLElement<user>

以下示例显示了如何获取<post>元素内的<user>元素计数:

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";
}

然后,这将为Number of posts: 1用户 ID 3 和Number of posts: 3用户 ID 8 显示。

但是,如果该外部 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()或者您也可以使用常见的 XML 解析器SimpleXML,甚至DOMDocument在 XML 的子集上。

于 2013-03-15T23:09:23.550 回答
0

您可以使用 PHP SimpleDomHTML(用 PHP5+ 编写的 HTML DOM 解析器,让您以非常简单的方式操作 HTML!)您可以像使用 jQuery 一样查询数据。它支持 HTML,因此可以肯定它很好地支持 XML 文档。

您可以在此处下载和查看文档:http: //simplehtmldom.sourceforge.net/

于 2013-03-15T02:58:35.863 回答