2013-03-15 139 views
4

我有以下結構的XML文檔:PHP XML Expat解析器:如何只讀取部分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>"; 
} 
+0

使用['XMLReader'](http://php.net/book.xmlreader)而不是expat解析器可以嗎? – hakre 2013-03-15 17:08:30

+0

我更喜歡使用Expat解析器,它是PHP本地的,可以處理大型XML文件,它也是基於事件的解析器而不是DOM。我發現它非常強大,我特別喜歡'xml_set_element_handler'函數,它可以幫助您輕鬆定義開始和結束標籤。我確定必須有一個選項來閱讀文檔的一部分! – razzak 2013-03-15 17:51:00

+0

'XMLReader'是PHP原生的,可以處理大型XML文件,它是一個XML Pull解析器。閱讀器充當向文檔流前進的光標,並停在路上的每個節點上。而對於Expat:不,沒有這樣的選擇,但對於XMLReader有這個;)這就是爲什麼我問。 – hakre 2013-03-15 17:55:31

回答

8

如提出了意見的前面,您也可以使用該XMLReaderDocs

XMLReader擴展是一個XML Pull解析器。閱讀器充當向文檔流前進的光標,並停在路上的每個節點上。

這是一個可以打開文件的類(同名:XMLReader)。默認情況下,您使用next()移動到下一個節點。然後,您將檢查當前位置是否在元素處,然後如果元素具有您要查找的名稱,然後可以處理它,例如通過讀取元素XMLReader::readOuterXml()Docs的外部XML。

與Expat解析器中的回調相比,這有點麻煩。爲了獲得更多的靈活性,我通常會自己創建iterators that are able to work on the XMLReader object and provide the steps I need

它們允許直接使用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文件是通過它打開:

$users = new XMLElementIterator($reader, 'user'); 

然後被過濾爲id屬性:

$reader = new XMLReader(); 
$reader->open($xmlFile); 

下一步在這位讀者的所有<user>元素創建一個迭代器先前存儲在陣列中的值:

$filteredUsers = new XMLAttributeFilter($users, 'id', $ids); 

其餘正在迭代foreach現在因爲所有條件已經制定:

foreach ($filteredUsers as $user) { 
    printf("---------------\nUser with ID %d:\n", $user->getAttribute('id')); 
    echo $user->readOuterXml(), "\n"; 
} 

將返回用戶的XML,ID分別爲3和8:

--------------- 
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這是the XMLReader iterators一部分確實也提供情況下SimpleXMLElementDocs您想要輕鬆讀取<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"; 
} 

這然後顯示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()穿越,也可以使用,以及像SimpleXML常見的XML解析器或者甚至在XML的子集上的DOMDocument

+0

它的工作原理,但它將結果打印在一行中:'client1 - message1 - time1 - client2 - message2 - time2 ....'有沒有一種方法可以自定義輸出,如'if($ client =「operater」){echo message time)} else {... }'? – razzak 2013-03-16 11:25:46

+0

當然,你不受輸出的限制。我只是在示例中使用純文本來保持它很小,但如果您願意,可以使用HTML。 – hakre 2013-03-16 11:28:57

+0

我試過這個'if($ post-> getChildElements('client')==「operater」){...} else {...}'this'if($ post-> getChildElements('client')) - > item(0)==「operater」){...} else {...}'in'foreach($ filteredUsers as $ user)''但它似乎不工作! – razzak 2013-03-16 11:49:22

0

您可以使用PHP SimpleDomHTML(A HTML DOM解析器寫在PHP5 +讓你在一個非常簡單的方法操作HTML!)你可以查詢你的數據,如您使用jQuery的工作方式。它支持HTML,因此它確實很好地支持XML文檔。

你可以在這裏下載和查看文檔:http://simplehtmldom.sourceforge.net/