Извлечение HTML из файла XML с помощью simpleXML

Я читаю xml-файл, созданный сторонним приложением, который включает в себя следующее:

<Cell> <Comment ss:Author="Mark Baker"> <ss:Data xmlns="http://www.w3.org/TR/REC-html40"><B><Font html:Face="Tahoma" html:Size="8" html:Color="#000000">Mark Baker:</Font></B><Font html:Face="Tahoma" html:Size="8" html:Color="#000000">
Comment 1 - No align</Font></ss:Data> </Comment> </Cell> 

То, что я пытаюсь сделать, – это доступ к необработанным данным из элемента Cell-> Comment-> Data либо «как есть», либо как фактический блок (X) HTML-разметки (желательно последний).

 if (isset($cell->Comment)) { echo 'comment found<br />'; $commentAttributes = $cell->Comment->attributes($namespaces['ss']); if (isset($commentAttributes->Author)) { echo 'Author: ',(string)$commentAttributes->Author,'<br />'; } $commentData = $cell->Comment->children($namespaces['ss']); var_dump($commentData); echo '<br />'; } 

дает мне:

 comment found Author: Mark Baker object(SimpleXMLElement)#130 (2) { ["@attributes"]=> array(1) { ["Author"]=> string(10) "Mark Baker" } ["Data"]=> object(SimpleXMLElement)#129 (0) { } } 

в то время как

 if (isset($cell->Comment)) { echo 'comment found<br />'; $commentAttributes = $cell->Comment->attributes($namespaces['ss']); if (isset($commentAttributes->Author)) { echo 'Author: ',(string)$commentAttributes->Author,'<br />'; } $commentData = $cell->Comment->Data->children(); var_dump($commentData); echo '<br />'; } 

дает мне:

 comment found Author: Mark Baker object(SimpleXMLElement)#129 (2) { ["B"]=> object(SimpleXMLElement)#118 (1) { ["Font"]=> string(11) "Mark Baker:" } ["Font"]=> string(21) " Comment 1 - No align" } 

К сожалению, simpleXML, кажется, обрабатывает весь элемент как ряд узлов XML. Я уверен, что я должен получить это необработанные данные без сложного цикла или подачи элемента в DOM Parser; возможно, используя пространство имен xmlns = "http://www.w3.org/TR/REC-html40", чтобы извлечь это чисто, но я не могу понять, как это сделать.

Любая помощь оценивается.

Более сложный пример XML-данных:

 <Cell> <Comment ss:Author="Mark Baker"> <ss:Data xmlns="http://www.w3.org/TR/REC-html40"> <B><Font html:Face="Tahoma" html:Size="8" html:Color="#000000">Mark Baker:</Font></B><Font html:Face="Tahoma" html:Size="8" html:Color="#000000">
</Font><B><Font html:Face="Tahoma" x:Family="Swiss" html:Size="8" html:Color="#000000">Rich </Font><U><Font html:Face="Tahoma" x:Family="Swiss" html:Size="8" html:Color="#FF0000">Text </Font></U><Font html:Face="Tahoma" x:Family="Swiss" html:Size="8" html:Color="#000000">Comment</Font></B><Font html:Face="Tahoma" html:Size="8" html:Color="#000000"> Center Aligned</Font> </ss:Data> </Comment> </Cell> 

Если ваша реализация должна была использовать DOM , я считаю, что вы могли бы сделать следующее:

 //given $node is <ss:data> $frag = $node->ownerDocument->createDocumentFragment(); foreach($node->childNodes as $child){ $frag->appendChild($child->cloneNode(true)); } $string = $node->ownerDocument->saveXML($frag); 

Если HTML внутри элемента <ss:Data> считается строковым литералом, его необходимо обернуть в раздел CDATA, как уже было намечено в комментариях

 $xml = <<< XML <Cell> <Comment ss:Author="Mark Baker"> <ss:Data xmlns="http://www.w3.org/TR/REC-html40"> <![CDATA[ <B><Font html:Face="Tahoma" … html:Color="#000000"> ]]> </ss:Data> </Comment> </Cell> XML; libxml_use_internal_errors(TRUE); $cell = simplexml_load_string($xml); echo $cell->Comment->Data; 

Если он не находится в секции CDATA, он будет считаться узлами. Затем вы будете искать innerXml <ss:Data> чтобы получить это как необработанный XML. К сожалению, ни SimpleXml, ни DOM не имеют собственного способа получить это непосредственно. Вы должны использовать реализацию userland.

В реализациях userland innerXml обычно либо перебираются по всем дочерним узлам, либо конкатенация их исходного XML. Или они сбрасывают все дерево и строку, заменяя корневой узел. Или они создают фрагмент или импортируют узлы в другой документ.

Я не знаю другого способа сделать это. Не уверен, что это возможно с помощью XSLT . XMLReader имеет метод readInnerXML .

На данный момент я пошел с быстрым и грязным решением. В долгосрочной перспективе я переключусь на использование XMLReader (по всем указанным причинам) … У меня просто нет времени переписать весь существующий код simpleXML на данный момент.

Я пошел с:

 $node = $cell->Comment->Data->asXML(); $comment = substr($node,49,-10); $comment = strip_tags($comment); 

Хотя я бы предпочел сохранить разметку HTML, для этого потребуется дополнительная работа, поэтому я просто удаляю все разметки, оставляя меня простым текстом (который является критическим элементом).

Хотя это далеко не идеальное решение, оно делает то, что мне нужно для этого (на данный момент), и я могу перейти к следующему элементу в моем списке «делать», добавив новый элемент «переписать с помощью XMLReader "в этот список.

Спасибо за помощь. Я обязательно вернусь к этой теме, когда я сделаю это переписывание.

Поэтому я знаю, что ваш вопрос пришел и ушел, но у меня была такая же проблема, и я должен был понять, как я хотел бы справиться с этим. Для будущих поколений, вот как я его получил.

Если вы принимаете только (x) HTML:

 $data = str_replace('<?xml version="1.0"?>','',$xmlNode->asXML()); 

Если вы считаете, что кто-то собирается поместить в XML, и вы в порядке с этим, вы только хотите убить первый, автоматически созданный тег XML:

 $data = preg_replace('/^<\?xml version="1.0"\?\>\n/', '',$xmlNode->asXML()); 

Таким образом, ваш код будет выглядеть так:

 if (isset($cell->Comment)) { echo 'comment found<br />'; $commentAttributes = $cell->Comment->attributes($namespaces['ss']); if (isset($commentAttributes->Author)) { echo 'Author: ',(string)$commentAttributes->Author,'<br />'; } $commentData = str_replace('<?xml version="1.0"?>','',$cell->Comment->Data->asXML()); echo $commentData; echo '<br />'; }