PHP xpath无法定位节点

时间:2016-04-15 19:01:23

标签: php xml xpath

给出以下片段:

<svrl:failed-assert>
    <svrl:text>message</svrl:text>
        <svrl:diagnostic>
            <diag xmlns="example2.com">
                <record>
                    <record.01>141</record.01>
                </record>
            </diag>
        </svrl:diagnostic>
</svrl:failed-assert>

我能够使用xpath找到所有failed-assert个节点:

$xml = simplexml_load_file($my_path);
$xml->registerXPathNamespace('svrl', 'example.com');
$errors = $xml->xpath('//svrl:failed-assert');

然后我遍历每一个并试图找到孩子:

foreach($errors as $error) {
     $text = $error->xpath('svrl:text'); //works fine
     $record = $error->xpath('svrl:diagnostic/diag/record'); //FAILS
}

无论我尝试什么,在什么组合中,我都无法获取xpath表达式来正确定位diagrecord节点。

我认为这可能与子节点没有命名空间,而父节点是事实有关。我无法控制源文档命名空间。

是否可以使用xpath表达式找到有问题的节点?

4 个答案:

答案 0 :(得分:1)

那是因为diag元素声明了默认名称空间,其URI是"example2.com"。请注意,没有前缀的后代元素会隐式地继承祖先的默认命名空间

您需要将前缀映射到默认命名空间,并使用前缀来引用该命名空间中的元素:

foreach($errors as $error) 
{
     $error->registerXPathNamespace('foo', 'example2.com');
     $text = $error->xpath('svrl:text'); 
     $record = $error->xpath('svrl:diagnostic/foo:diag/foo:record');
}

答案 1 :(得分:0)

你可以试试像

这样的XPath
svrl:diagnostic/*[namespace-uri()='example2.com' and local-name()='diag']/*[namespace-uri()='example2.com' and local-name()='record']

当然不是很漂亮,但应该做好这份工作。

答案 2 :(得分:0)

使用这个:

//*[namespace-uri()='example2.com']/*[name()='record.01']/text()

你得到141

答案 3 :(得分:0)

处理xpath是XML最大的痛苦之一。除了痛苦之外,它也是一个非常缓慢的处理。(比阵列慢100倍)。

在与xpath战斗多年后,我决定不再使用它了,我将XML转换为Json以便快速工作......

取决于您的需求,但您可能需要考虑此选项。

用法:

$var = '<svrl:failed-assert>
<svrl:text>message</svrl:text>
    <svrl:diagnostic>
        <diag xmlns="example2.com">
            <record>
                <record.01>141</record.01>
            </record>
        </diag>
    </svrl:diagnostic>
</svrl:failed-assert>';

$result=xmlstr_to_array($var);

将输出:

array(2) { ["svrl:text"]=> string(7) "message" ["svrl:diagnostic"]=> array(1) { ["diag"]=> array(1) { ["record"]=> array(1) { ["record.01"]=> string(3) "141" } } } }

功能:

// load xml string
function xmlstr_to_array($xmlstr) {
    $doc = new DOMDocument();
    $doc->loadXML($xmlstr);
    return domnode_to_array($doc->documentElement);
}

// convert nodes
function domnode_to_array($node) {
$output = array();
switch ($node->nodeType) {
    case XML_CDATA_SECTION_NODE:
    case XML_TEXT_NODE:
        $output = trim($node->textContent);
        break;
    case XML_ELEMENT_NODE:
        for ($i = 0, $m = $node->childNodes->length; $i < $m; $i++) {
            $child = $node->childNodes->item($i);
            $v = domnode_to_array($child);
            if (isset($child->tagName)) {
                $t = ConvertTypes($child->tagName);
                if (!isset($output[$t])) {
                    $output[$t] = array();
                }
                $output[$t][] = $v;
            } elseif ($v) {
                $output = (string) $v;
            }
        }
        if (is_array($output)) {
            if ($node->attributes->length) {
                $a = array();
                foreach ($node->attributes as $attrName => $attrNode) {
                    $a[$attrName] = ConvertTypes($attrNode->value);
                }
                $output['@attributes'] = $a;
            }
            foreach ($output as $t => $v) {
                if (is_array($v) && count($v) == 1 && $t != '@attributes') {
                    $output[$t] = $v[0];
                }
            }
        }
        break;
}
return $output;
}

//convert types
function ConvertTypes($org) {
if (is_numeric($org)) {
    $val = floatval($org);
} else {
    if ($org === 'true') {
        $val = true;
    } else if ($org === 'false') {
        $val = false;
    } else {
        if ($org === '') {
            $val = null;
        } else {
            $val = $org;
        }
    }
}
return $val;
}