给出以下片段:
<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表达式来正确定位diag
或record
节点。
我认为这可能与子节点没有命名空间,而父节点是事实有关。我无法控制源文档命名空间。
是否可以使用xpath表达式找到有问题的节点?
答案 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)
你可以试试像
这样的XPathsvrl: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;
}