SimpleXML получает элемент Элемент между дочерними элементами

Я анализирую XML на PHP с помощью SimpleXML и имею такой XML:

<xml> <element> textpart1 <subelement>subcontent1</subelement> textpart2 <subelement>subcontent2</subelement> textpart3 </element> </xml> 

Когда я делаю $xml->element он, естественно, дает мне весь элемент, как и во всех трех текстовых частях.

Поэтому, если я разбираю это в массив (с foreach для детей), я получаю:

 0 => textpart1textpart2textpart3, 1 => subcontent1, 2 => subcontent2 

Мне нужен способ разобрать узел <element> чтобы каждая текстовая часть, которая останавливалась или начиналась после того, как подэлемент рассматривается как свой собственный элемент.

В результате я ищу упорядоченный список, который может быть выражен в таком массиве:

 0 => textpart1, 1 => subcontent1, 2 => textpart2, 3 => subcontent2, 4 => textpart3 

Возможно ли это без изменения файла XML? Спасибо заранее за любые подсказки!

Как говорили другие, SimpleXML не имеет поддержки для доступа к отдельным текстовым узлам в качестве отдельных объектов, поэтому вам нужно будет дополнить его некоторыми методами DOM. К счастью, вы можете переключаться между ними по собственному желанию, используя dom_import_simplexml и simplexml_import_dom .

Ключевыми элементами функциональности DOM, которые вам нужны, являются:

  • переменная-член DOMElement-> childNodes для доступа ко всем узлам непосредственно под определенным элементом в качестве итерируемого списка
  • переменная DOMNode-> nodeType для определения того, является ли конкретный дочерний элемент текстовым узлом или элементом
  • переменная DOMNode-> nodeValue для получения фактического текста

Учитывая это, вы можете написать функцию, которая возвращает массив со смесью объектов 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)); 

Вот живая демонстрация этой последней функции.

Простой ответ – нет. SimpleXML не реализует никакой поддержки текстовых узлов.
В этом случае ваш лучший и предпочтительный вариант – использовать DOMDocument .

Вы действительно ищете все текстовые узлы, которые являются потомками узла element элемента. Это можно выразить следующим образом:

 /*/element//text() 

Даже SimpleXML имеет метод xpath , который выполняет этот запрос без каких-либо ошибок, фактические текстовые узлы преобразуются в узлы их родительских элементов. Это связано с тем, как работает SimpleXML и для чего он предназначен.

Сравнить с:

  • Какие DOMNodes могут быть представлены SimpleXMLElement?
  • Отдельные текстовые узлы с доступом к SimpleXML
  • Re [4]: ​​[PHP-DEV] SimpleXML-> children () и текстовые узлы

Однако, с некоторой помощью 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 ) 

Это возможно из-за класса DOMXPath внутренне завершает DOMXPath и DOMXPath результат в случае, если он является текстовым:

 /** * 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; } }