-->

PHP的XML Expat解析器:如何读取XML文档中的一部分?(PHP XML Expat par

2019-08-17 15:14发布

我有以下结构的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"))

任何帮助,将不胜感激

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

Answer 1:

作为一个评论所说较早,则可以选择使用XMLReader 文档

XMLReader可以扩展是一个XML pull解析器。 读者充当光标放在文档流前进,并在该方式,每个节点上终止。

这是一类(具有相同的名称: XMLReader ),它可以打开一个文件。 默认情况下,您使用next()移动到下一个节点。 这样,你会检查当前的位置是一个元素,那么如果元素有你要找的名称,然后你可以通过读取元件的外部XML处理它,例如XMLReader::readOuterXml() 文档

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

这将返回一个带有编号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 文档中要容易地将内部读值的情况下<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的一个子集。



Answer 2:

您可以使用PHP SimpleDomHTML(写在PHP5 +让你在一个非常简单的方法操作HTML一个HTML DOM解析器!)你可以查询你的数据,如您使用jQuery的工作方式。 它支持HTML,以便为XML文档确保它很好的支持。

你可以在这里下载和查看文档: http://simplehtmldom.sourceforge.net/



文章来源: PHP XML Expat parser: how to read only part of the XML document?