PHP SimpleXML получает innerXML

Мне нужно получить содержимое HTML- answer в этом бите XML:

 <qa> <question>Who are you?</question> <answer>Who who, <strong>who who</strong>, <em>me</em></answer> </qa> 

Поэтому я хочу получить строку «Кто, кто, <strong>, кто </ strong>, <em> me </ em>".

Если у меня есть answer как SimpleXMLElement , я могу вызвать asXML() чтобы получить «<answer> Кто, кто <strong>, кто </ strong>, <em> me </ em> </ answer>", но как получить внутренний XML элемента без того, чтобы сам элемент был обернут вокруг него?

Я бы предпочел способы, которые не включают строковые функции, но если это единственный способ, пусть будет так.

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

 include 'SimpleDOM.php'; $qa = simpledom_load_string( '<qa> <question>Who are you?</question> <answer>Who who, <strong>who who</strong>, <em>me</em></answer> </qa>' ); echo $qa->answer->innerXML(); 

В противном случае я вижу два способа сделать это. Первым будет преобразование вашего SimpleXMLElement в DOMNode затем цикл над его childNodes для построения XML. Другой – вызвать asXML() затем использовать строковые функции для удаления корневого узла. Тем не менее, asXML() внимание, что asXML() иногда может возвращать разметку, которая фактически находится за пределами узла, из которого он был вызван, например, пролог XML или Инструкции по обработке.

 function SimpleXMLElement_innerXML($xml) { $innerXML= ''; foreach (dom_import_simplexml($xml)->childNodes as $child) { $innerXML .= $child->ownerDocument->saveXML( $child ); } return $innerXML; }; 

Это работает (хотя кажется, что он очень хромает):

 echo (string)$qa->answer; 

наиболее простым решением является внедрение пользовательского get innerXML с простым XML:

 function simplexml_innerXML($node) { $content=""; foreach($node->children() as $child) $content .= $child->asXml(); return $content; } 

В коде замените $body_content = $el->asXml(); с $body_content = simplexml_innerXML($el);

Однако вы также можете переключиться на другой API, который предлагает различие между innerXML (то, что вы ищете) и внешнимXML (что вы получаете сейчас). Microsoft Dom libary предлагает это различие, но, к сожалению, PHP DOM этого не делает.

Я обнаружил, что PHP XMLReader API предлагает это задание. См. ReadInnerXML (). Хотя этот API имеет совершенно иной подход к обработке XML. Попробуй.

Наконец, я хотел бы подчеркнуть, что XML не предназначен для извлечения данных в виде поддеревьев, а скорее как значения. Вот почему вам сложно найти правильный API. Было бы более «стандартным» хранить HTML-поддерево как значение (и избегать всех тегов), а не поддерево XML. Также будьте осторожны, что некоторые синтаксисы HTML не всегда совместимы с XML (т.е.
vs,
). Во всяком случае, на практике вы подходите, определенно, более удобно для редактирования xml-файла.

Я бы расширил класс SimpleXmlElement:

 class MyXmlElement extends SimpleXMLElement{ final public function innerXML(){ $tag = $this->getName(); $value = $this->__toString(); if('' === $value){ return null; } return preg_replace('!<'. $tag .'(?:[^>]*)>(.*)</'. $tag .'>!Ums', '$1', $this->asXml()); } } 

и затем используйте его следующим образом:

 echo $qa->answer->innerXML(); 
 <?php function getInnerXml($xml_text) { //strip the first element //check if the strip tag is empty also $xml_text = trim($xml_text); $s1 = strpos($xml_text,">"); $s2 = trim(substr($xml_text,0,$s1)); //get the head with ">" and trim (note that string is indexed from 0) if ($s2[strlen($s2)-1]=="/") //tag is empty return ""; $s3 = strrpos($xml_text,"<"); //get last closing "<" return substr($xml_text,$s1+1,$s3-$s1-1); } var_dump(getInnerXml("<xml />")); var_dump(getInnerXml("<xml / >faf < / xml>")); var_dump(getInnerXml("<xml >< / xml>")); var_dump(getInnerXml("<xml>faf < / xml>")); var_dump(getInnerXml("<xml > faf < / xml>")); ?> 

После того, как я искал какое-то время, я не получил удовлетворительного решения. Поэтому я написал свою собственную функцию. Эта функция получит точное содержимое innerXml (включая, конечно, пробел). Чтобы использовать его, передайте результат функции asXML() , как этот getInnerXml($e->asXML()) . Эта функция работает и для элементов со многими префиксами (как и в моем случае, поскольку я не мог найти какие-либо текущие методы, которые делают преобразование на всех дочерних узлах разных префиксов).

Вывод:

 string '' (length=0) string '' (length=0) string '' (length=0) string 'faf ' (length=4) string ' faf ' (length=6) 
  function get_inner_xml(SimpleXMLElement $SimpleXMLElement) { $element_name = $SimpleXMLElement->getName(); $inner_xml = $SimpleXMLElement->asXML(); $inner_xml = str_replace('<'.$element_name.'>', '', $inner_xml); $inner_xml = str_replace('</'.$element_name.'>', '', $inner_xml); $inner_xml = trim($inner_xml); return $inner_xml; } 

Если вы не хотите снимать секцию CDATA, прокомментируйте строки 6-8.

 function innerXML($i){ $text=$i->asXML(); $sp=strpos($text,">"); $ep=strrpos($text,"<"); $text=trim(($sp!==false && $sp<=$ep)?substr($text,$sp+1,$ep-$sp-1):''); $sp=strpos($text,'<![CDATA['); $ep=strrpos($text,"]]>"); $text=trim(($sp==0 && $ep==strlen($text)-3)?substr($text,$sp+9,-3):$text); return($text); } 

Вы можете просто использовать эту функцию 🙂

 function innerXML( $node ) { $name = $node->getName(); return preg_replace( '/((<'.$name.'[^>]*>)|(<\/'.$name.'>))/UD', "", $node->asXML() ); } 

используя regex, вы можете сделать это

 preg_match('/<answer(.*)?>(.*)?<\/answer>/', $xml, $match); $result=$match[0]; print_r($result);