Как отличить объекты SimpleXML, представляющие элемент и атрибут? - PullRequest
3 голосов
/ 07 апреля 2009

Мне нужно печатать произвольные объекты 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's $node->nodeType или $node instanceof DOMAttr? (Вместо этого я не могу использовать DOM, поддержка SimpleXML является основным требованием).

Ответы [ 6 ]

3 голосов
/ 12 февраля 2013

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

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

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

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

Так что, если вам нужно это точное значение с быстрым логическим значением 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)    |
+------------------+---------------------------------------------+
2 голосов
/ 18 мая 2009

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

function is_attribute($node) {
    return !($node->asXML()[0] == "<")
}
2 голосов
/ 16 мая 2009

Да, есть способ. Что ж, ничего изящного, что вы можете получить через 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"

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

1 голос
/ 15 ноября 2009

Есть ли скрытое свойство / метод, который позволяет идентифицировать тип узла в 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);
1 голос
/ 07 апреля 2009

Вам нужно SimpleXMLElement :: attribute :

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 . '>';

}

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

0 голосов
/ 15 мая 2009

К сожалению, нет скрытого свойства или метода, позволяющего идентифицировать тип узла в 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)
...