我使用SimpleXML在PHP中解析XML,并且有这样的XML:
<xml>
<element>
textpart1
<subelement>subcontent1</subelement>
textpart2
<subelement>subcontent2</subelement>
textpart3
</element>
</xml>
当我做$xml->element
时,它自然地给了我整个元素,就像在所有三个textparts中一样。
因此,如果我将其解析为一个数组(子项为foreach
),我得到:
0 => textpart1textpart2textpart3, 1 => subcontent1, 2 => subcontent2
我需要一种解析<element>
节点的方法,以便在子元素处停止或开始的每个textpart被视为自己的元素。
因此,我正在寻找一个可以在这样的数组中表达的有序列表:
0 => textpart1, 1 => subcontent1, 2 => textpart2, 3 => subcontent2, 4 => textpart3
这是否可以在不改变XML文件的情况下实现?提前感谢任何提示!
答案 0 :(得分:3)
正如其他人所说,SimpleXML并不支持将单个文本节点作为单独的实体进行访问,因此您需要使用某些DOM方法对其进行补充。值得庆幸的是,您可以使用dom_import_simplexml
和simplexml_import_dom
随意切换两者。
您需要的DOM功能的关键部分是:
鉴于这些,您可以编写一个函数,该函数返回一个数组,其中包含子元素的SimpleXML对象和子文本节点的字符串,如下所示:
function get_child_elements_and_text_nodes($sx_element)
{
$return = array();
$dom_element = dom_import_simplexml($sx_element);
foreach ( $dom_element->childNodes as $dom_child )
{
switch ( $dom_child->nodeType )
{
case XML_TEXT_NODE:
$return[] = $dom_child->nodeValue;
break;
case XML_ELEMENT_NODE:
$return[] = simplexml_import_dom($dom_child);
break;
}
}
return $return;
}
在你的情况下,你需要递归树,如果你把DOM和SimpleXML混合在一起会让你有点混乱,所以你可以完全在DOM中编写递归并在运行它之前转换SimpleXML对象:
function recursively_find_text_nodes($dom_element)
{
$return = array();
foreach ( $dom_element->childNodes as $dom_child )
{
switch ( $dom_child->nodeType )
{
case XML_TEXT_NODE:
$return[] = $dom_child->nodeValue;
break;
case XML_ELEMENT_NODE:
$return = array_merge($return, recursively_find_text_nodes($dom_child));
break;
}
}
return $return;
}
$text_nodes = recursively_find_text_nodes(dom_import_simplexml($simplexml->element));
答案 1 :(得分:0)
简单的答案是否定的。 SimpleXML不对文本节点实现任何类型的支持 在这种情况下,您最好和首选的选项是使用DOMDocument。
答案 2 :(得分:0)
您实际上正在查找作为element
元素节点后代的所有文本节点。这可以表示为以下xpath:
/*/element//text()
即使SimpleXML有一个xpath
方法,它可以执行此查询而不会出现任何错误,实际的文本节点将转换为其父元素节点。这是因为SimpleXML的工作原理以及它的设计目的。
与:比较:
但是,在姐妹库DOMDocument的帮助下,它可以自己代表文本节点,可以让它工作:
<?php
/**
* SimpleXML get Element Content between Child Elements
* @link https://stackoverflow.com/q/20131226/367456
*/
$buffer = <<<BUFFER
<xml>
<element>
textpart1
<subelement>subcontent1</subelement>
textpart2
<subelement>subcontent2</subelement>
textpart3
</element>
</xml>
BUFFER;
$xml = simplexml_load_string($buffer);
$xpath = new SimpleXMLXpath($xml);
$result = $xpath->query('/*/element//text()');
print_r($result);
结果输出是:
Array
(
[0] =>
textpart1
[1] => subcontent1
[2] =>
textpart2
[3] => subcontent2
[4] =>
textpart3
)
这是可能的,因为SimpleXMLXpath
类在内部包装DOMXPath
并将结果字符串化,以防它是textnode:
/**
* Class SimpleXMLXpath
*
* @author hakre <http://hakre.wordpress.com/>
*/
class SimpleXMLXpath
{
private $xml;
public function __construct(SimpleXMLElement $xml)
{
$this->xml = $xml;
}
public function query($expression)
{
$context = dom_import_simplexml($this->xml);
$xpath = new DOMXPath($context->ownerDocument);
$result = [];
foreach ($xpath->query($expression, $context) as $node) {
switch (TRUE) {
case $node instanceof DOMText:
$result[] = $node->nodeValue;
continue;
case $node instanceof DOMElement:
case $node instanceof DOMAttr:
$result[] = simplexml_import_dom($node);
continue;
}
}
return $result;
}
}