什么是解析XML中混合文本和元素标记的最简单方法?

时间:2014-05-14 23:34:32

标签: php xml

我知道这些问题已经存在多个问题,但我找不到足够接近问题的问题。我想解析一些看起来像这样的XML。只有少数元素(可能只有<text/>将具有混合标记,其余元素都可以使用SimpleXML轻松解析:

<root>
  <element>
    <text>A <x>b</x> c <y>d</y> e.</text>
  </element>
</root>

我已经在大多数结构中使用SimpleXML,但是,当我到达<text/>元素时,我不知道如何单独阅读这些部分(即&#34; { {1}}&#34;,&#34; A&#34;&amp;&#34; c&#34;应为文字e.&amp; {{ 1}}应该是元素)并按从左到右的顺序。我所能做的就是获得没有标记的所有文本,或者只获取没有文本的子元素。如果在SimpleXML中无法做到这一点,我可以使用DOMXMLReader实现此目的吗?我一直试图将<x/>元素转换为DOMNodeList(所以在这个例子中我会有一个包含五个节点的列表)但到目前为止我还没有成功。我到目前为止所尝试的是:

<y/>

似乎不是一个返回特定元素的所有子节点(文本和标签)列表的方法。 PHP中是否有其他类可以完成我忽略的工作?据我所知,到目前为止,SimpleXML只能完全解析XML,其中每个元素只包含文本,只包含其他元素或为空。

2 个答案:

答案 0 :(得分:0)

以下代码使用XMLReaderXMLReader::read()XMLReader::nodeType执行我想要的操作:

<?php
$refl = new ReflectionClass('XMLReader');
$xml_consts = $refl->getConstants();
$xml = <<<XML
<root>
  <element>
    <text>A <x>b</x> c <y>d</y> e.</text>
  </element>
</root>
XML;
$reader = new XMLReader();
$reader->XML($xml);
// For validation only
$reader->setParserProperty(XMLReader::VALIDATE, true);
if ($reader->isValid()) {
    print("No matter what people say, this XML is valid!\n\n");
}
// Prevent warnings about missing DTD
$reader->setParserProperty(XMLReader::VALIDATE, false);
while ($reader->read()) {
    $info = ': ';
    switch ($reader->nodeType) {
        case XMLReader::TEXT:
            $info .= "'$reader->value'";
            break;
        case XMLReader::ELEMENT:
            $info .= "<$reader->name>";
            break;
        case XMLReader::END_ELEMENT:
            $info .= "</$reader->name>";
            break;
        default:
            $info = '';
    }
    print(array_search($reader->nodeType, $xml_consts)  . $info . PHP_EOL);
}
?>

输出:

No matter what people say, this XML is valid!

ELEMENT: <root>
SIGNIFICANT_WHITESPACE
ELEMENT: <element>
SIGNIFICANT_WHITESPACE
ELEMENT: <text>
TEXT: 'A '
ELEMENT: <x>
TEXT: 'b'
END_ELEMENT: </x>
TEXT: ' c '
ELEMENT: <y>
TEXT: 'd'
END_ELEMENT: </y>
TEXT: ' e.'
END_ELEMENT: </text>
SIGNIFICANT_WHITESPACE
END_ELEMENT: </element>
SIGNIFICANT_WHITESPACE
END_ELEMENT: </root>

答案 1 :(得分:0)

您也可以使用DOM + Xpath。以下示例遍历所有元素和文本节点。这种方式的好处是,您可以将任何节点用作其他Xpath表达式的上下文。

$xml = <<<'XML'
<root>
  <element>
    <text>A <x>b</x> c <y>d</y> e.</text>
  </element>
</root>
XML;

$dom = new DOMDocument();
$dom->loadXML($xml);
$xpath = new DOMXpath($dom);

$nodes = $xpath->evaluate(
 '//*|//text()[normalize-space(.) != ""]'
);

foreach ($nodes as $node) {
  switch ($node->nodeType) {
  case XML_ELEMENT_NODE :
    var_dump("ELEMENT: ".$node->localName);
    break;
  case XML_TEXT_NODE :
  case XML_CDATA_SECTION_NODE :
    var_dump("TEXT: ".$node->textContent);
    break;
  }
}

输出:https://eval.in/152418

string(13) "ELEMENT: root"
string(16) "ELEMENT: element"
string(13) "ELEMENT: text"
string(8) "TEXT: A "
string(10) "ELEMENT: x"
string(7) "TEXT: b"
string(9) "TEXT:  c "
string(10) "ELEMENT: y"
string(7) "TEXT: d"
string(9) "TEXT:  e."