6

主題の問題に対する解決策は 1 つありますが、これはハックであり、これを行うためのより良い方法があるかどうか疑問に思っています。

以下は、サンプルの XML ファイルと、引数として指定された xpath クエリを実行する PHP CLI スクリプトです。このテスト ケースのコマンド ラインは次のとおりです。

./xpeg "//MainType[@ID=123]"

最も奇妙に思えるのは次の行です。これがないと、私のアプローチは機能しません。

$result->loadXML($result->saveXML($result));

私の知る限り、これは単に変更された XML を再解析するだけであり、これは必要ではないように思えます。

PHP でこの XML に対して xpath クエリを実行するより良い方法はありますか?


XML (デフォルトの名前空間のバインディングに注意してください):

<?xml version="1.0" encoding="utf-8"?>
<MyRoot
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.example.com/data http://www.example.com/data/MyRoot.xsd"
 xmlns="http://www.example.com/data">
  <MainType ID="192" comment="Bob's site">
    <Price>$0.20</Price>
    <TheUrl><![CDATA[http://www.example.com/path1/]]></TheUrl>
    <Validated>N</Validated>
  </MainType>
  <MainType ID="123" comment="Test site">
    <Price>$99.95</Price>
    <TheUrl><![CDATA[http://www.example.com/path2]]></TheUrl>
    <Validated>N</Validated>
  </MainType>
  <MainType ID="922" comment="Health Insurance">
    <Price>$600.00</Price>
    <TheUrl><![CDATA[http://www.example.com/eg/xyz.php]]></TheUrl>
    <Validated>N</Validated>
  </MainType>
  <MainType ID="389" comment="Used Cars">
    <Price>$5000.00</Price>
    <TheUrl><![CDATA[http://www.example.com/tata.php]]></TheUrl>
    <Validated>N</Validated>
  </MainType>
</MyRoot>

PHP CLI スクリプト:

#!/usr/bin/php-cli
<?php

$xml = file_get_contents("xpeg.xml");

$domdoc = new DOMDocument();
$domdoc->loadXML($xml);

// remove the default namespace binding
$e = $domdoc->documentElement;
$e->removeAttributeNS($e->getAttributeNode("xmlns")->nodeValue,"");

// hack hack, cough cough, hack hack
$domdoc->loadXML($domdoc->saveXML($domdoc));

$xpath = new DOMXpath($domdoc);

$str = trim($argv[1]);
$result = $xpath->query($str);
if ($result !== FALSE) {
  dump_dom_levels($result);
}
else {
  echo "error\n";
}

// The following function isn't really part of the
// question. It simply provides a concise summary of
// the result.
function dump_dom_levels($node, $level = 0) {
  $class = get_class($node);
  if ($class == "DOMNodeList") {
    echo "Level $level ($class): $node->length items\n";
    foreach ($node as $child_node) {
      dump_dom_levels($child_node, $level+1);
    }
  }
  else {
    $nChildren = 0;
    foreach ($node->childNodes as $child_node) {
      if ($child_node->hasChildNodes()) {
        $nChildren++;
      }
    }
    if ($nChildren) {
      echo "Level $level ($class): $nChildren children\n";
    }
    foreach ($node->childNodes as $child_node) {
      if ($child_node->hasChildNodes()) {
        dump_dom_levels($child_node, $level+1);
      }
    }
  }
}
?>
4

4 に答える 4

13

解決策は名前空間を使用することであり、それを取り除くことではありません。

$result = new DOMDocument();
$result->loadXML($xml);

$xpath = new DOMXpath($result);
$xpath->registerNamespace("x", trim($argv[2]));

$str = trim($argv[1]);
$result = $xpath->query($str);

そして、コマンド ラインで次のように呼び出します ( x:XPath 式の に注意してください)。

./xpeg "//x:MainType[@ID=123]" "http://www.example.com/data"

これをより光沢のあるものにすることができます

  • 自分でデフォルトの名前空間を見つける (ドキュメント要素の名前空間プロパティを見て)
  • コマンドラインで複数の名前空間をサポートし、それらをすべて登録する前に$xpath->query()
  • xyz=http//namespace.uri/カスタム名前空間プレフィックスを作成するための形式での引数のサポート

要点: XPath では//foo、本当の意味でクエリを実行することはできません//namespace:foo。これらは根本的に異なるため、異なるノードを選択します。XML で既定の名前空間を定義できる (したがって、ドキュメントで明示的な名前空間の使用を削除できる) という事実は、XPath で名前空間の使用を削除できるという意味ではありません。

于 2011-06-25T03:14:00.437 に答える
1

好奇心から、この行を削除するとどうなりますか?

$e->removeAttributeNS($e->getAttributeNode("xmlns")->nodeValue,"");

ハッキングが必要になる可能性が最も高いと思います。基本的に、そのxmlns="http://www.example.com/data"部分を削除してから、DOMDocument を再構築しています。文字列関数を使用してその名前空間を削除することを検討したことはありますか?

$pieces = explode('xmlns="', $xml);
$xml = $pieces[0] . substr($pieces[1], strpos($pieces[1], '"') + 1);

その後、あなたの方法を続けますか?結果的に速くなる可能性さえあります。

于 2011-06-25T02:56:30.610 に答える