2013-11-03 53 views
1

我正在使用SimpleXML嘗試解析large XML file<!ENTITY聲明。不幸的是,SimpleXML似乎太渴望繼續並展開這些實體,而我寧願它沒有,因爲實體符號很短,易於解析,理論上在文件的較新版本中不會改變,而擴展實體是可能會改變的英語句子。有什麼方法可以告訴SimpleXML將它關閉嗎?SimpleXML,請不要展開實體

我已經想過「預解析」XML文件以在將文件內容傳遞給XML解析器之前去除<!ENTITY位,但這感覺很糟糕,而且由於它是一個巨大的文件,我寧願做盡可能少地擺弄它。

(請原諒任何錯誤的術語在上面。我沒有做過這種級別的XML工作在相當長的一段)

+0

是否有可能創建一個小樣本的XML和PHP代碼來顯示不良行爲?這將大大幫助他人理解問題並提出解決方案。 – IMSoP

+0

@IMSoP:好評,最終沒有多少意義,儘管這是可能的。只是留下了廣泛的答案。 – hakre

+0

[PHP SimpleXML獲取innerXML]的可能重複(http://stackoverflow.com/questions/1937056/php-simplexml-get-innerxml) – hakre

回答

3

這似乎如此,但除非你指定的標誌是不是這樣的(這我想你不會在代碼中顯示你所做的事情)。只是如果您使用->asXML()方法而不是通過字符串實現,SimpleXML只能將它返回給您。

讓我們來演示一下它的工作原理。

<!ENTITY n "noun (common) (futsuumeishi)"> 

所以我們選擇第一<pos>因素,因爲它包含一個&n;實體:

$xml = simplexml_load_file($file); 
$pos = $xml->entry->sense->pos; 

變量$pos現在是<pos>元素的SimpleXMLElement我從DTD選擇這個簡單的實體節點。讓我們將它輸出看到解析器確實與&n;實體是什麼:

echo "SimpleXML value (string): ", $pos   , "\n" 
    , "SimpleXML value (XML) : ", $pos->asXML(), "\n"; 

輸出是:

SimpleXML value (string): noun (common) (futsuumeishi) 
SimpleXML value (XML) : <pos>&n;</pos> 

這個例子說明,在&n;仍然存在(<pos>&n;</pos>),它只是它會在你訪問它的那一刻作爲字符串值進行擴展(noun (common) (futsuumeishi))。

順便提一下,XML規範在這裏說的是,解析器是否需要擴展這些實體。對於SimpleXML的設計,在閱讀字符串值時,這完全有望得到擴展。

你甚至可以通過指定LIBXML_NOENT選項控制這一行爲:

$xml = simplexml_load_file($file, NULL, LIBXML_NOENT); 

這實際上將你承擔那什麼,實體正在擴大,XML輸出不包含實體不再:

SimpleXML value (string): noun (common) (futsuumeishi) 
SimpleXML value (XML) : <pos>noun (common) (futsuumeishi)</pos> 

所以現在雙問號怎麼辦,你在找什麼?那麼,PHP中的XML解析器實際上有一個實體模型是DOMDocument。它是SimpleXML的姊妹庫,在內部都共享相同的內存對象。下面是同一個對象的輸出(更準確地說:其唯一的子節點)爲這兩個模式,而不與LIBXML_NOENT

Mode 1: 
DOMDocument Class  : DOMEntityReference 
DOMDocument value(XML) : &n; 
DOMDocument ->nodeName : n 

Mode 2 (LIBXML_NOENT): 
DOMDocument Class  : DOMText 
DOMDocument value(XML) : noun (common) (futsuumeishi) 
DOMDocument ->nodeName : #text 

這是由下面的代碼應該讓更多的可見背後是什麼賦予創建輸出:

$node = dom_import_simplexml($pos); 
$doc = $node->ownerDocument; 
$entity = $node->firstChild; 

echo "DOMDocument Class  : ", get_class($entity) , "\n" 
    , "DOMDocument value(XML) : ", $doc->saveXML($entity), "\n" 
    , "DOMDocument ->nodeName : ", $entity->nodeName  , "\n"; 

書面這是一個姐姐圖書館和dom_import_simplexml$posDOMElement,而我們需要遍歷它的孩子們,我們知道是有問題的實體引用。

所以現在這個開始製作完美的意義:由於SimpleXML的無法表示實體引用,它只能提供擴展的字符串值包含實體的XML。

,否則這將是對不同的

<pos>&n;</pos> 
<pos><![CDATA[&n;]]></pos> 

字符串值的方式嗎?所以你要求的只是有限的意義。然而,這並不意味着我們無法處理這個問題,因此可以通過擴展SimpleXML來欺騙SimpleXML。假設每個只包含單個實體的子元素都應該返回。否則,標準的SimpleXML stringyfication應使用:

/** 
* Class EntityPreserveXML 
*/ 
class EntityPreserveXML extends SimpleXMLElement 
{ 
    /** 
    * @return string 
    */ 
    public function __toString() 
    { 
     $dom = dom_import_simplexml($this); 
     if (
      !$dom instanceof DOMElement 
      || $dom->childNodes->length !== 1 
      || ! $dom->firstChild instanceof DOMEntityReference 
     ) { 
      return parent::__toString(); 
     } 

     return $dom->ownerDocument->saveXML($dom->firstChild); 
    } 
} 

算了,我們的例子來看,從上面:

require('EntityPreserveXML.php'); 
$xml = simplexml_load_file($file, 'EntityPreserveXML'); 
$pos = $xml->entry->sense->pos; 

echo "SimpleXML value (string): ", $pos   , "\n" 
    , "SimpleXML value (XML) : ", $pos->asXML(), "\n"; 

的SimpleXML現在使用擴展的類,然後給出預期:

SimpleXML value (string): &n; 
SimpleXML value (XML) : <pos>&n;</pos> 

&n;因爲它是唯一的孩子現在保存在SimpleXMLElement的字符串轉換中。但是,僅僅因爲這個工作並不意味着你應該使用它,它打破了文本形式的解析XML和文檔模型意義上的XML之間的編碼邊界。

也許你只是在尋找DOMDocument?這是一個有更多細節的模型,如果有的話,您可以使用DOMEntityReference

+2

一個坦率而含糊的問題的出色徹底答案!在某種程度上,它對於[LJXML_NOCDATA]的實際意義是什麼以及它如何與實體處理有關怎麼做是一個很好的伴侶[這個我後來寫的](http://stackoverflow.com/a/13981917/157957) 。 – IMSoP

+0

我可以在這裏使用DOMDocument嗎?嗯,我沒有考慮它,因爲我的印象是它只是用於處理HTML或其他行爲像瀏覽器。也許我會研究它。或者,也許我會堅持使用SimpleXML,使用asXML(),並使用(gasp!)正則表達式從結果中抽取符號。感謝您分享您對我的選擇的瞭解。 –

+0

@IMSoP:謝謝,這是一個很好的交叉。有一天,我們可能會收集更好的答案。我想我們肯定已經有很多年了。 – hakre