使用PHP SimpleXML Xpath解析使用命名空间的XML时遇到问题

时间:2016-02-04 11:59:31

标签: php xpath namespaces simplexml

有一些与PHP和SimpleXML有关的命名空间的帖子,但没有一个人看到解决我遇到的挑战。这是一小段代表我身体较大的机构的XML,尽管如此,挑战是相同的。我无法获得任何xpath查询来返回我想要的数据。请注意以下

$xml = <<<EOD
<blah:book xmlns:chap="http://example.org/chapter-title" xmlns:blah="urn:blah">
    <blah:wrap>
        <chap:wrap>
            <title>My Book</title>
            <chapter id="1">
                <title>Chapter 1</title>
                <para>Donec velit. Nullam eget tellus vitae</para>
            </chapter>
            <chapter id="2">
                <title>Chapter 2</title>
                <para>Lorem ipsum dolor sit amet</para>
            </chapter>
        </chap:wrap>
    </blah:wrap>
</blah:book>
EOD;

在第一个机构中出现的“blah”和“chap”的名称空间似乎没有出现问题。如果我用xpath运行以下php代码查询以下结果:

$sxe = new SimpleXMLElement($xml);
$result = $sxe->xpath('/node()/*/*');
var_dump($result);

// gives me
array(1) {
  [0]=>
  object(SimpleXMLElement)#2 (2) {
    ["title"]=>
    string(7) "My Book"
    ["chapter"]=>
    array(2) {
      [0]=>
      object(SimpleXMLElement)#4 (3) {
        ["@attributes"]=>
        array(1) {
          ["id"]=>
          string(1) "1"
        }
        ["title"]=>
        string(9) "Chapter 1"
        ["para"]=>
        string(37) "Donec velit. Nullam eget tellus vitae"
      }
      [1]=>
      object(SimpleXMLElement)#5 (3) {
        ["@attributes"]=>
        array(1) {
          ["id"]=>
          string(1) "2"
        }
        ["title"]=>
        string(9) "Chapter 2"
        ["para"]=>
        string(26) "Lorem ipsum dolor sit amet"
      }
    }
  }
}

当“chap”更为普遍时会出现问题:

$xml = <<<EOD
<blah:book xmlns:chap="http://example.org/chapter-title" xmlns:blah="urn:blah">
    <blah:wrap>
        <chap:wrap>
            <chap:title>My Book</chap:title>
            <chap:chapter id="1">
                <chap:title>Chapter 1</chap:title>
                <chap:para>Donec velit. Nullam eget tellus vitae</chap:para>
            </chap:chapter>
            <chap:chapter id="2">
                <chap:title>Chapter 2</chap:title>
                <chap:para>Lorem ipsum dolor sit amet</chap:para>
            </chap:chapter>
        </chap:wrap>
    </blah:wrap>
</blah:book>
EOD;

之后,上面相同的php代码导致了这种结构:

array(1) {
  [0]=>
  object(SimpleXMLElement)#2 (0) {
  }
}

我尝试过注册命名空间:

$sxe = new SimpleXMLElement($xml);
$sxe->registerXPathNamespace('chap', 'http://example.org/chapter-title');
$result = $sxe->xpath('/node()/*/*');
var_dump($result);

但结果仍然相同:

array(1) {
  [0]=>
  object(SimpleXMLElement)#2 (0) {
  }
}

我已经尝试了许多不同的xpath查询,这些查询都不会从xml的第一个主体返回数组结构中的整个xml主体。这是一对夫妇,但我已经尝试了绝望的事情,但没有一个有效。

$result = $sxe->xpath('/node()/chap:*/*');
$result = $sxe->xpath('/node()/*/chap:*');

有些帖子建议删除所有名称空间然后不必担心它,但是,应该有一种方法来解析它,就像在第一个例子中可能的那样检索整个主体。不幸的是我空手而归。我也承认,我不明白为什么在第一个体中出现'chap'命名空间不会导致初始xpath查询出现问题。我希望有人能指出我正确的方向。

1 个答案:

答案 0 :(得分:0)

也许一种可能的解决方案是将其用作xpath表达式:

  

$ result = $ sxe-&gt; xpath(&#39; // blah:book / blah:wrap&#39;);

xpath返回一个数组,您可以从中获取第一个项目。这将是SimpleXMLElement类型的形式,您可以使用children方法并传递命名空间。

您可以在foreach中循环子项,$res变量的类型为SimpleXMLElement。然后,您可以检查属性是否已设置并获取数据。

例如:

$xml = <<<EOD
<blah:book xmlns:chap="http://example.org/chapter-title" xmlns:blah="urn:blah">
    <blah:wrap>
        <chap:wrap>
            <chap:title>My Book</chap:title>
            <chap:chapter id="1">
                <chap:title>Chapter 1</chap:title>
                <chap:para>Donec velit. Nullam eget tellus vitae</chap:para>
            </chap:chapter>
            <chap:chapter id="2">
                <chap:title>Chapter 2</chap:title>
                <chap:para>Lorem ipsum dolor sit amet</chap:para>
            </chap:chapter>
        </chap:wrap>
    </blah:wrap>
</blah:book>
EOD;

$sxe = new SimpleXMLElement($xml);

$result = $sxe->xpath('//blah:book/blah:wrap');
foreach ($result[0]->children('http://example.org/chapter-title') as $res) {
    if (isset($res->title)) {
        $bookTitle = $res->title->__toString();
    }
    if (isset($res->chapter)) {
        foreach ($res->chapter as $chapter) {
            $chapterTitle = $chapter->title->__toString();
            $chapterPara = $chapter->para->__toString();
        }
    }
}

Demo