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>, я em>".
Если у меня есть answer
как SimpleXMLElement
, Я могу позвонить asXML()
получить "
Я бы предпочел способы, которые не включают строковые функции, но если это единственный путь, пусть будет так.
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 или инструкции обработки.
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 (что вы ищете) и outerXML (что вы получаете сейчас). Библиотека Microsoft Dom предлагает это различие, но, к сожалению, PHP DOM этого не делает.
Я обнаружил, что PHP XMLReader API предлагает это различие. Смотрите readInnerXML(). Хотя этот API имеет совсем другой подход к обработке XML. Попытайся.
Наконец, я хотел бы подчеркнуть, что XML предназначен не для извлечения данных в виде поддеревьев, а в качестве значения. Вот почему у вас возникают проблемы с поиском подходящего API. Было бы более "стандартным" хранить поддерево HTML как значение (и экранировать все теги), а не поддерево XML. Также имейте в виду, что некоторые синтаксисы HTML не всегда совместимы с XML (т.е.
против
). Во всяком случае, на практике вы подходите, безусловно, удобнее для редактирования 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();
Вот очень быстрое решение, которое я создал:
function InnerHTML($Text)
{
return SubStr($Text, ($PosStart = strpos($Text,'>')+1), strpos($Text,'<',-1)-1-$PosStart);
}
echo InnerHTML($yourXML->qa->answer->asXML());
Если вы не хотите удалять раздел 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 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;
}
<?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 innerXML( $node )
{
$name = $node->getName();
return preg_replace( '/((<'.$name.'[^>]*>)|(<\/'.$name.'>))/UD', "", $node->asXML() );
}
Используя регулярные выражения вы могли бы сделать это
preg_match(’/<answer(.*)?>(.*)?<\/answer>/’, $xml, $match);
$result=$match[0];
print_r($result);