Как отличить объекты SimpleXML от элемента и атрибута?

Мне нужно печатать произвольные объекты SimpleXML определенным образом, с особой обработкой узлов атрибутов.

Проблема состоит в том, что элементы и атрибуты SimpleXML, похоже, используют точно такой же класс, узел атрибутов даже претендует на поддержку метода attributes() , а SimpleXML скрывает свои внутренние компоненты, поэтому, похоже, нет способа рассказать тип узла (короткий генерации XML и репарации его).

Оба дают одинаковый результат:

 $element = new SimpleXMLElement('<foo>test</foo>'); echo $element; print_r($element); $element = new SimpleXMLElement('<foo attr="test" />'); echo $element['attr']; print_r($element['attr']); 

Есть ли скрытое свойство / метод, который позволяет идентифицировать тип узла в SimpleXML? Эквивалент DOM $node->nodeType или $node instanceof DOMAttr ? (Я не могу использовать DOM вместо этого, поддержка SimpleXML является основным требованием).

В SimpleXMLElement нет встроенных свойств, которые позволят вам рассказать об этом отдельно.

Однако, по мнению других, dom_import_simplexml может быть уместным, однако, эта функция может иногда менять узлы «на лету», например, если вы передаете список дочерних узлов или именованных дочерних узлов, они будут принимать их и превращать в первый элемент.

Если это пустой список, например, никаких атрибутов, возвращаемых из attributes() или несуществующих именованных дочерних узлов, он выдаст предупреждение о том, что вам был указан недопустимый тип узла:

Предупреждение: dom_import_simplexml (): неверный тип Nodetype для импорта

Поэтому, если вам нужна эта точность с быстрым логическим значением true / false , вот как это работает с Simplexml:

 $isElement = $element->xpath('.') == array($element); $isAttribute = $element[0] == $element and $element->xpath('.') != array($element); 

Он работает аналогично спискам атрибутов и спискам элементов, о которых я только что писал об этом утром , вам нужно иметь определенные знания о том, что оценивать для чего, поэтому я создал для него чит-лист:

 +------------------+---------------------------------------------+ | TYPE | TEST | +------------------+---------------------------------------------+ | Element | $element->xpath('.') == array($element) | +------------------+---------------------------------------------+ | Attribute | $element[0] == $element | | | and $element->xpath('.') != array($element) | +------------------+---------------------------------------------+ | Attributes | $element->attributes() === NULL | +------------------+---------------------------------------------+ | Elements | $element[0] != $element | | | and $element->attributes() !== NULL | +------------------+---------------------------------------------+ | Single | $element[0] == $element | +------------------+---------------------------------------------+ | Empty List | $element[0] == NULL | +------------------+---------------------------------------------+ | Document Element | $element->xpath('/*') == array($element) | +------------------+---------------------------------------------+ 
  • SimpleXML Type Cheatsheet (12 февраля 2013; by hakre)

Да, есть способ. Ну, ничего элегантного, что вы можете получить через API, но где-то в кишки SimpleXML отслеживает, что это такое, и он создает различия, например, когда вы вызываете такие функции, как getName () или asXML ().

 $element = new SimpleXMLElement('<foo>test</foo>'); print_r($element->getName()); print_r($element->asXML()); echo "------------------\n"; $element = new SimpleXMLElement('<foo attr="test" />'); $at = $element['attr']; print_r($at->getName()); print_r($at->asXML()); foo <?xml version="1.0"?> <foo>test</foo> ------------------ attr attr="test" 

Ваш код не выиграет конкурс красоты, но по крайней мере вы можете это сделать.

Используя то, что указал палако, может работать такая функция:

 function is_attribute($node) { return !($node->asXML()[0] == "<") } 

Вам нужны атрибуты SimpleXMLElement :::

 function xml_out($el) { $name = $el->getName(); echo '<'. $name; if (count($el->attributes())) { foreach ($el->attributes() as $a=>$v) { echo ' '. ((string)$a) . '="' . htmlspecialchars((string)$v) . '"'; } } echo '>'. (string)$el; if (count($el->children())) { foreach($el->children() as $c) { xml_out($c); } } echo '</'. $name . '>'; } 

Может потребоваться немного подстройки.

Есть ли скрытое свойство / метод, который позволяет идентифицировать тип узла в SimpleXML? Эквивалент DOM $ node-> nodeType или $ node instanceof DOMAttr? (Я не могу использовать DOM вместо этого, поддержка SimpleXML является основным требованием)

Ответ – нет … и да. SimpleXML не обладает таким свойством, но вот хорошая новость: SimpleXML и DOM – это две грани одной и той же монеты. (монета – libxml;)) Вам не нужно выбирать тот или иной, вы можете использовать оба! Вы можете превратить SimpleXMLElement в DOMNode и наоборот. В вашем случае вы можете сделать что-то вроде:

 $element = new SimpleXMLElement('<foo attr="test" />'); $is_attr = (dom_import_simplexml($element['attr'])->nodeType === XML_ATTRIBUTE_NODE); 

Если вы часто это делаете или не хотите обрабатывать жонглирование DOM / SimpleXML, вы можете взглянуть на SimpleDOM .

 $element = new SimpleDOM('<foo attr="test" />'); $is_attr = ($element['attr']->nodeType() === XML_ATTRIBUTE_NODE); 

К сожалению, нет скрытого свойства или метода, который позволяет идентифицировать тип узла в SimpleXML. SimpleXML использует только один класс, и у элементов нет ничего, что указывало бы на их родителей. Если вы попытаетесь найти отражение ниже, вы увидите, что нет ничего, чтобы отличать элемент или атрибут.

 $element = new SimpleXMLElement('<foo>test</foo>'); echo ReflectionObject::export($element); $element = new SimpleXMLElement('<foo attr="test">test</foo>'); echo ReflectionObject::export($element['attr']); 

Однако, если элемент имеет атрибуты, вы можете обнаружить это. Таким образом, вы должны были бы предположить, что все элементы, прошедшие через, имеют атрибуты. С этим предположением вы могли бы рассказать им обособленно.

 $element = new SimpleXMLElement('<foo attr="test">test</foo>'); echo ReflectionObject::export($element); echo ReflectionObject::export($element['attr']); 

Это лучшее, что я могу придумать. Помните, что элемент без атрибутов по-прежнему возвращает false с этим.

 function isNotAttribute($simpleXML) { return (count($simpleXML->attributes()) > 0); } $element = new SimpleXMLElement('<foo attr="test">test</foo>'); var_dump(isNotAttribute($element)); var_dump(isNotAttribute($element['attr'])); // returns bool(true) bool(false)