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(), чтобы получить «‹ответ›Кто кто, ‹сильный›кто кто‹/сильный›, ‹em›me‹/em›‹/ответ›», но как получить внутренний XML элемента без самого элемента, обернутого вокруг него?

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


person Bart van Heukelom    schedule 20.12.2009    source источник


Ответы (10)


Насколько мне известно, нет встроенного способа получить это. Я бы порекомендовал попробовать 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() может иногда возвращать разметку, которая на самом деле находится вне узла, из которого он был вызван, например XML-пролог или инструкции по обработке.

person Josh Davis    schedule 21.12.2009

Это работает (хотя кажется действительно хромым):

echo (string)$qa->answer;
person scrooloose    schedule 02.09.2010
comment
Совсем не халявный! избавил меня от жонглирования xml несколькими переменными. Я видел ламера ;) - person Aries VII; 12.07.2013

самое простое решение - реализовать пользовательский 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 (то, что вы ищете) и externalXML (то, что вы получаете сейчас). Библиотека Microsoft Dom предлагает это различие, но, к сожалению, PHP DOM этого не делает.

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

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

person Frederic Bazin    schedule 13.06.2011
comment
Спасибо за это, но есть одна проблема, пример кода немного сломан, $node не определен. - person Brian Wigginton; 13.07.2012

Я бы расширил класс 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();
person lingtalfi    schedule 24.08.2012

<?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)
person user1240602    schedule 29.02.2012

Если вы не хотите удалять секцию 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);
}
person Pavel Sedek    schedule 20.03.2014

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

function innerXML( $node )
{
    $name = $node->getName();
    return preg_replace( '/((<'.$name.'[^>]*>)|(<\/'.$name.'>))/UD', "", $node->asXML() );
}
person Олег Всильдерев&    schedule 19.06.2014

Вот очень быстрое решение, которое я создал:

function InnerHTML($Text)
{   
    return SubStr($Text, ($PosStart = strpos($Text,'>')+1), strpos($Text,'<',-1)-1-$PosStart);
}

echo InnerHTML($yourXML->qa->answer->asXML());
person Taurus    schedule 04.12.2020

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

preg_match(’/<answer(.*)?>(.*)?<\/answer>/’, $xml, $match);
$result=$match[0];
print_r($result);
person streetparade    schedule 20.12.2009
comment
Это определенно неправильный вариант использования регулярного выражения. Никогда не следует использовать его для разбора xml/dom. не говоря уже о том, что $match[0] всегда содержит полный текст для поиска. А $xml — это объект, а не строка. - person Rápli András; 28.11.2014

person    schedule
comment
отличное, простое решение! - person Wes; 10.07.2019