如何区分表示元素和属性的SimpleXML对象?

时间:2009-04-07 10:12:36

标签: php reflection 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是核心要求)。

6 个答案:

答案 0 :(得分:3)

SimpleXMLElement中没有内置属性可以让您区分它们。

正如其他人所建议的那样dom_import_simplexml可能是合适的,但是,该函数有时可以动态更改节点,例如,如果传入子节点列表或命名的子节点,它将采用这些并转换它们进入第一个元素。

如果它是一个空列表,例如没有从attributes()或不存在的命名子节点返回的属性,它将发出警告,告诉您已经给出了无效的节点类型:

  

警告:dom_import_simplexml():要导入的节点类型无效

因此,如果您需要使用snappy布尔值true / false进行精确定位,以下是Simplexml的工作原理:

$isElement   = $element->xpath('.') == array($element);

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

它与属性列表和元素列表的工作方式类似,我just blogged about this in the morning,你需要具备一些关于什么要评估什么的具体知识,所以我为它创建了一个备忘单:

+------------------+---------------------------------------------+
| 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)    |
+------------------+---------------------------------------------+

答案 1 :(得分:2)

是的,有办法。好吧,没有什么优雅的东西可以通过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"

你的代码不会赢得选美比赛,但至少你可以做到。

答案 2 :(得分:2)

使用palako指出的内容,这样的函数可能会起作用:

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

答案 3 :(得分:1)

您需要SimpleXMLElement::attributes

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

}

可能需要一些调整。

答案 4 :(得分:1)

  

是否存在允许在SimpleXML中识别节点类型的隐藏属性/方法?相当于DOM的$ node-&gt; 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);

答案 5 :(得分:0)

不幸的是,没有隐藏的属性或方法允许在SimpleXML中识别节点的类型。 SimpleXML只使用一个Class,而元素没有任何指向其父级的东西。如果您尝试以下反射,您将看到没有任何东西可以区分元素或属性。

$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)