代码之家  ›  专栏  ›  技术社区  ›  Kornel

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

  •  3
  • Kornel  · 技术社区  · 16 年前

    我需要打印 任意的 以特定方式处理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 回复  |  直到 16 年前
        1
  •  3
  •   hakre    12 年前

    中没有内置属性 SimpleXMLElement 这可以让你把这些分开。

    正如其他人所说 dom_import_simplexml 但是,该函数可能是适当的,有时可以动态地更改节点,例如,如果传入子节点列表或命名的子节点,它将获取这些节点并将它们转换为第一个元素。

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

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

    所以如果你需要一个快速的布尔值 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)    |
    +------------------+---------------------------------------------+
    
        2
  •  2
  •   palako    16 年前

    是的,有办法。好吧,通过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"
    

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

        3
  •  2
  •   doomspork    16 年前

    帕拉科指出,像这样的功能可能有效:

    function is_attribute($node) {
        return !($node->asXML()[0] == "<")
    }
    
        4
  •  1
  •   Milen A. Radev    16 年前

    你需要 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 . '>';
    
    }
    

    可能需要一些调整。

        5
  •  1
  •   Josh Davis    15 年前

    是否存在允许在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);
    
        6
  •  0
  •   gradbot    16 年前

    不幸的是,在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)