我需要以特定方式打印任意 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是核心要求)。
答案 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)