php SimpleXML 检查孩子是不是存在

Posted

技术标签:

【中文标题】php SimpleXML 检查孩子是不是存在【英文标题】:php SimpleXML check if a child existsphp SimpleXML 检查孩子是否存在 【发布时间】:2010-12-06 08:37:06 【问题描述】:

A->b->c 可能存在,但c 可能不存在。如何查看?

【问题讨论】:

请选择一个新答案 【参考方案1】:

最好将其包装在isset()

if(isset($A->b->c))  // c exists

这样,如果 $A$A->b 不存在......它不会爆炸。

【讨论】:

这似乎比其他答案更整洁,但实际上 SimpleXMLElement 返回并为任何不存在的请求节点清空 SimpleXMLElement 对象。所以 empty() 方法似乎是最好的方法。 另外,c 可能是可选的,但 Ab 可能是必需的,因此如果未定义它们,我实际上可能希望获得一个例外 - 检查文档完整性的简单方法 @spikyjt isset() 是检查节点是否存在的最佳途径。如果您需要检查节点是否存在并且里面有东西,您可以使用empty(),即在><(文本或子节点)之间。如果您只需要检查节点是否存在,那么empty() 将不会这样做。例如,empty() 将为此节点返回 true <b a="zzz"></b> @CITBL 你是对的,empty() 没有多大用处。然而,重要的是要注意,如果没有 c 元素,isset($A->c) === false,但 $A->c 返回一个空的 SimpleXMLElement。所以使用$c = $A->c;,然后是isset($c) === true(即$c !== null,尽管这可能是预期的)。【参考方案2】:

SimpleXML 总是返回对象。如果没有子对象,则返回空对象。

if( !empty($a->b))
  var_dump($a->b);

【讨论】:

显然这是一个特性而不是一个错误。注意到这一点非常重要。如果对象不存在,则访问对象的子对象将创建它。 这应该是公认的答案,会为我节省一些时间和挫败感:-) 错误答案。因为empty()即使节点存在但没有内容也会返回true。例如,在您的代码示例中,如果$a 包含以下内容:<xml><b a="zzz"/></xml>,那么empty($a->b) 将返回true。因此,这不是检查节点是否存在的可靠方法。你可以使用empty(),但如果你需要一个节点内的东西,即在><之间,并且对没有内容的节点不感兴趣【参考方案3】:

我通过使用children() 函数并在其上执行count() 来解决它,如果没有子级,则通过在计数调用之前放置一个@ 来忽略php 错误。这很愚蠢,但它有效:

$identification = $xml->identification;
if (@count($identification->children()) == 0)
  $identification = $xml->Identification;

我讨厌这个..

【讨论】:

你是完全正确的(这就是为什么我说'我讨厌这个......'),但是当你知道发生任何错误时,你希望它被忽略,那么这很好。跨度> 【参考方案4】:

经过一些实验,我发现检查节点是否存在的唯一可靠方法是使用count($xml->someNode)

这是一个测试用例:https://gist.github.com/Thinkscape/6262156

【讨论】:

【参考方案5】:

@null 的答案是正确的 - 最简单的方法是使用 isset

if (isset($A->b->c))  /* c exists */ 

但是,其原因并不明显(至少对我而言),而且此页面上存在相当多的错误信息。我花了一段时间才正确理解它,所以我想分享我学到的东西。

正如一些人所指出的,当您访问一个不存在的 SimpleXMLElement 子元素时,您得到的实际上是一个“空”的 SimpleXMLElement,而不是 false 或 null。

例如,如果 b 不存在:

$b = $A->b; // $b is now an empty SimpleXMLElement
$b->getName(); // returns an empty string
isset($b); // returns true

所以你可能会认为使用isset来测试children是否存在是行不通的,因为如果children不存在,我们仍然会得到一个空的SimpleXMLObject,所以isset必然会返回true .

但实际上并没有:

isset($A->b); // returns false

这让我很惊讶!原因是isset 不是常规函数,而是 PHP 语言构造。当您调用isset($A->b) 时,PHP 不会先计算$A->b,然后将结果作为参数传递给isset()。相反,在不可访问的对象属性上调用 isset 时的行为是调用类上的 __isset() 重载方法(如下所述:https://www.php.net/manual/en/language.oop5.overloading.php#object.isset)

所以类的作者可以独立于$b = $A->b的结果控制isset($A->b)的结果。在 SimpleXMLElement 的情况下,他们将其设置为 isset($A->b) 如果 b 存在则返回 true,否则返回 false - 这正是我们测试子元素是否存在所需要的。

对此还有一个脚注——最初的问题是关于测试$A->b->c 的存在性。即使中间 b 不存在,使用 isset($A->b->c) 也可以完美地解决这个问题。我认为这里发生的事情是PHP首先执行$A->b,如果b不存在,它会得到一个空的SimpleXMLElement,然后在那个空的SimpleXMLElement上调用__isset('c')以获得isset($A->b->c)的最终结果。

【讨论】:

【参考方案6】:

如果你有 PHP 5.3,你可以使用$a->count()。否则,scippie 使用@count($a->children()) 的解决方案效果很好。我发现我不需要 @ 但较旧的 PHP 实现可能需要它。

【讨论】:

这应该是接受的答案,目前接受的是错误的。【参考方案7】:

方法 xpath 返回匹配元素的数组或 false

if(false !== $A->xpath('b/c'))  ...

http://www.php.net/manual/ru/simplexmlelement.xpath.php

【讨论】:

不,文档表明如果出现错误,它会返回 FALSE,而不是如果路径没有返回结果。 测试过这个,@Brian 是正确的,只有出错时才会返回false。不要使用这个检查,它总是会返回一个(空)节点。【参考方案8】:

简单

var_dump(count($xml->node));

【讨论】:

在 PHP7.2 中,他们在计算未实现 Countable 接口的内容时添加了警告。因此,应该考虑到这一点,并更好地使用 isset() 而不是 count()【参考方案9】:

使用if(isset($A->b) 给了我问题,所以我尝试了 if($A->b) 并且成功了!

【讨论】:

count 和强制转换为布尔值是有意义的两种方式。正如您所描述的那样使用if 是转换为布尔值的最短方法。【参考方案10】:

使用 xpath:

function has_child(\SimpleXMLElement $parent=null, string $xpathToChild)

    return isset($parent) && !empty($parent->xpath('('.$xpathToChild.')[1]'));

其中$parent 是要检查的子节点的间接或直接父节点,$xpathToChild 是子节点相对于$parent 的 xpath。

()[1] 是因为我们不想选择所有子节点。一个就够了。

检查 $a->b->c 是否存在:

has_child($a,'b/c');

您还可以检查属性。检查节点c是否有t属性。

has_child($a,'b/c/@t');

【讨论】:

【参考方案11】:

我可以在 PHP 5.5.23 中确认工作的 3 种方法是使用 isset() count()empty()

这是一个显示每个结果的脚本:

https://gist.github.com/mchelen/306f4f31f21c02cb0c24

【讨论】:

【参考方案12】:

我使用辅助函数来检查节点是否是函数中作为参数提供的有效节点。

private static function isValidNode($node) 
  return isset($node) && $node instanceof SimpleXMLElement && !empty($node);

使用示例:

public function getIdFromNode($node) 
  if (!self::isValidNode($node)) 
    return 0;
  
  return (int)$node['id'];

【讨论】:

【参考方案13】:

我想分享一下我的经验。在 5.4 上运行,我尝试使用 'isset' 和 'empty' 进行测试,但都不适合我。我最终使用了 is_null

if(!is_null($xml->scheduler->outterList->innerList)) 
    //do something

【讨论】:

【参考方案14】:

命名空间

请注意,如果您在 XML 文件中使用名称空间,则在检查子项时需要在函数调用中包含这些名称空间,否则每次都会返回零:

if ($XMLelement->children($nameSpace,TRUE)->count())
    //do something here 

【讨论】:

【参考方案15】:

我不能说早期版本,但在 PHP 8.0 中,使用以下一行函数:

function elementExists(?SimpleXMLElement $element) : bool 
  return is_null($element)?false:@count($element);


$A = new SimpleXMLElement('<A><b><c/></b></A>');  // doc contains c
$B = new SimpleXMLElement('<B><b/></B>');         // doc does not contain c 
$C = new SimpleXMLElement('<C><x><c/></x></C>');  // doc contains c but different heirarchy

print '$A contains ->b->c : ' . (elementExists($A->b->c)?"true":"false") . PHP_EOL;
print '$B contains ->b->c : ' . (elementExists($B->b->c)?"true":"false") . PHP_EOL;
print '$C contains ->b->c : ' . (elementExists($C->b->c)?"true":"false") . PHP_EOL;

返回

$A contains ->b->c : true
$B contains ->b->c : false
$C contains ->b->c : false

即。正确判断 c 是否存在并在所需位置。

【讨论】:

【参考方案16】:

你可以试试:

if($A->b->c && $A->b->c != '')

【讨论】:

【参考方案17】:
if($A->b->c != null) //c exists

如果c 不存在,则其值为null(或者更准确地说,它没有值)。但是请注意,要使其正常工作,Ab 都不需要是 null。否则,PHP 会抛出错误(我认为)。

【讨论】:

这不是个好主意。如果节点不存在,c 将是一个空对象,这与 NULL 不同。 补充@spikyjt所说的,如果节点c在$A->b中不存在,则返回一个空的SimpleXMLElement。 SimpleXMLElement 的有效实例不为空;该表达式的计算结果始终为 true。

以上是关于php SimpleXML 检查孩子是不是存在的主要内容,如果未能解决你的问题,请参考以下文章

检查孩子是不是存在不适用于AngularFire2

Google firebase 检查孩子是不是存在

检查是不是有孩子在场?如果仅存在则执行特定方法

如何使用angularfire2检查孩子是不是存在于firebase列表中

PHP 文件检查总是返回 false

错误:“输入不是正确的 UTF-8,表示编码!”使用 PHP 的 simplexml_load_string