【问题标题】:Using XPath to extract XML in PHP在 PHP 中使用 XPath 提取 XML
【发布时间】:2015-12-05 18:58:01
【问题描述】:

我有以下 XML:

<root>
   <level name="level1">
       <!-- More children <level> --> 
   </level>

   <level name="level2"> 
       <!-- Some more children <level> --> 
   </level> 
</root>

如何直接在&lt;root&gt; 下提取&lt;level&gt;,以便可以相对于提取的&lt;level&gt; 运行诸如$xml-&gt;xpath('//some-query') 之类的XPath 查询?

【问题讨论】:

  • 不明白。您希望 Xpath 获得一级节点吗? /root/level[1]
  • 您是否有特殊原因希望/需要将这些查询分成两部分? (可能有....)
  • 我设计的XPath 仅在根目录中只有一个&lt;level&gt; 时才有效。
  • 您可以通过添加谓词来将其限制/固定到一级元素,例如level[@name="level2"]您的查询。
  • 我唯一的问题是我的 XPath 查询应该遵循这一步,首先在 xml 中识别叶节点:stackoverflow.com/questions/32478608/…

标签: php xml xpath


【解决方案1】:

DOMXpath::evaluate() 允许您从 DOM 中获取节点列表和标量值。

因此您可以直接使用 Xpath 表达式获取值:

$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);

var_dump(
  $xpath->evaluate('string(/root/level[@name="level2"]/@name)')
);

输出:

string(6) "level2"

Xpath 表达式

root 中的所有level 元素节点:
/root/level

具有特定名称属性的:
/root/level[@name="level2"]

您要获取的值(name 属性用于验证):
/root/level[@name="level2"]/@name

转换成字符串,如果找到节点,结果将是一个空字符串:

string(/root/level[@name="level2"]/@name)

遍历节点,将它们用作上下文

如果您需要为节点执行多个表达式,最好单独获取它并使用foreach()DOMXpath::evaluate() 的第二个参数是上下文节点。

foreach ($xpath->evaluate('/root/level[@name="level2"]') as $level) {
  var_dump(
    $xpath->evaluate('string(@name)', $level)
  );
}

节点列表长度

如果您需要处理未找到节点的问题,您可以检查DOMNodeList::$length 属性。

$levels = $xpath->evaluate('/root/level[@name="level2"]');
if ($levels->length > 0) {
  $level = $levels->item(0);
  var_dump(
    $xpath->evaluate('string(@name)', $level)
  );
} else {
  // no level found
}

count() 表达式

您也可以使用 count() 表达式验证这里是之前的元素。

var_dump(
  $xpath->evaluate('count(/root/level[@name="level2"])')
);

输出:

float(1)

布尔结果

可以在 Xpath 中设置一个条件并返回布尔值。

var_dump(
  $xpath->evaluate('count(/root/level[@name="level2"]) > 0')
);

输出:

bool(true)

【讨论】:

    【解决方案2】:

    这应该可行:

    $dom = new DOMDocument;
    $dom->loadXML($xml);
    $levels = $dom->getElementsByTagName('level');
    
    foreach ($levels as $level) {
       $levelname = $level->getAttribute('name');
          if ($levelname == 'level1') {
            //do stuff
          } 
    }
    

    我个人更喜欢 DOMNodeList 类来解析 XML。

    【讨论】:

    • 元素的名称是level - 不是level1level1 是属性name 的“唯一”一个值。
    • 据我了解,OP 只需要第一个。在这种情况下,他将在元素内使用name 标签。
    • ...但是 getElementsByTagName 不会以这种方式进行。 name 是一个属性,level1 是示例文档中属性 name 的值之一。 getElementsByTagName 不关心属性。示例文档中没有 &lt;level1&gt;element -> getElementsByTagName('level1') 将返回一个空列表。
    • @VolkerK 你是完全正确的。我修改了我的答案。感谢您的观察。
    • ;-) 现在是问题的on which I can run an XPath query such as $xml-&gt;Path('\\some-query') 部分。我的猜测是这是真正的问题,而不是如何“找到/提取”一个特定的关卡元素。但是,是的,这是猜测。
    【解决方案3】:

    使用 querypath 解析 XML/HTML 让这一切变得超级简单。

    $qp = qp($xml) ;
    $levels = $qp->find('root')->eq(0)->find('level') ;
    
    foreach($levels as $level ){
        //do  whatever you want with it , get its xpath , html, attributes etc.
        $level->xpath() ; //
    }
    

    Excellent beginner tutorial for Querypath

    【讨论】:

    • 查询路径看起来非常有用。你怎么只匹配某个属性的find('level'),比如leve1l?它基于什么类型的许可?
    • 我猜应该是-&gt;find('level[name="level1"]'),即元素level 的css3 选择器,其属性为name,值为level1 - 除了属性名称前没有@在这种情况下,它与 xpath 查询相同。
    • 在 foreach 循环中获取级别 usibg $level->attr('name') 的属性名称,然后将其与正则表达式进行比较。我不知道为什么我被否决了,但查询部分非常通用
    【解决方案4】:

    DOMXPath::query 的第二个参数是上下文节点。只需传递您之前“找到”的 DOMNode 实例,您的查询就会“相对”于该节点运行。例如

    <?php
    $doc = new DOMDocument;
    $doc->loadxml( data() );
    
    $xpath = new DOMXPath($doc);
    $nset = $xpath->query('/root/level[@name="level1"]');
    if ( $nset->length < 1 ) {
        die('....no such element');
    }
    else {
        $elLevel = $nset->item(0);
    
        foreach( $xpath->query('c', $elLevel) as $elC) {
            echo $elC->nodeValue, "\r\n";
        }
    }
    
    
    function data() {
        return <<< eox
    <root>
        <level name="level1">
            <c>C1</c>
            <a>A</a>
            <c>C2</c>
            <b>B</b>
            <c>C3</c>
        </level>
        <level name="level2"> 
            <!-- Some more children <level> --> 
        </level> 
    </root>
    eox;
    }
    

    但除非您必须执行多个单独(可能很复杂)的后续查询,否则这很可能没有必要

    <?php
    $doc = new DOMDocument;
    $doc->loadxml( data() );
    
    $xpath = new DOMXPath($doc);
    foreach( $xpath->query('/root/level[@name="level1"]/c') as $c ) {
        echo $c->nodeValue, "\r\n"; 
    }
    
    
    function data() {
        return <<< eox
    <root>
        <level name="level1">
            <c>C1</c>
            <a>A</a>
            <c>C2</c>
            <b>B</b>
            <c>C3</c>
        </level>
        <level name="level2"> 
            <c>Ahh</c>
            <a>ouch</a>
            <c>no</c>
            <b>wrxl</b>
        </level> 
    </root>
    eox;
    }
    

    仅使用一个查询具有相同的输出。

    【讨论】:

      猜你喜欢
      • 2011-12-21
      • 2020-03-05
      • 2015-12-23
      • 1970-01-01
      • 2019-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多