PHP xpath无法定位节点
给定以下片段:PHP xpath无法定位节点,php,xml,xpath,Php,Xml,Xpath,给定以下片段: <svrl:failed-assert> <svrl:text>message</svrl:text> <svrl:diagnostic> <diag xmlns="example2.com"> <record> <record.01>141</record.01>
<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>
然后我循环遍历每一个并尝试查找子项:
foreach($errors as $error) {
$text = $error->xpath('svrl:text'); //works fine
$record = $error->xpath('svrl:diagnostic/diag/record'); //FAILS
}
无论我尝试使用何种组合,都无法使xpath表达式正确定位diag
或record
节点
我假设这可能与以下事实有关:子节点没有名称空间,而父节点没有名称空间。我无法控制源文档名称空间
是否可以使用xpath表达式定位有问题的节点?您可以尝试类似的xpath表达式
svrl:diagnostic/*[namespace-uri()='example2.com' and local-name()='diag']/*[namespace-uri()='example2.com' and local-name()='record']
当然不是很漂亮,但应该可以完成这项工作。使用以下方法:
//*[namespace-uri()='example2.com']/*[name()='record.01']/text()
处理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);
职能:
// 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;
}
//加载xml字符串
函数xmlstr_to_数组($xmlstr){
$doc=新的DOMDocument();
$doc->loadXML($xmlstr);
将domnode_返回到_数组($doc->documentElement);
}
//转换节点
函数domnode_到_数组($node){
$output=array();
开关($node->nodeType){
案例XML\U CDATA\U节\U节点:
案例XML_文本_节点:
$output=trim($node->textContent);
打破
案例XML_元素_节点:
对于($i=0,$m=$node->childNodes->length;$i<$m;$i++){
$child=$node->childNodes->item($i);
$v=domnode_到_数组($child);
if(isset($child->tagName)){
$t=ConvertTypes($child->tagName);
如果(!isset($output[$t])){
$output[$t]=array();
}
$output[$t][]=$v;
}埃尔塞伊夫(五美元){
$output=(字符串)$v;
}
}
if(is_数组($output)){
如果($node->attributes->length){
$a=数组();
foreach($node->属性为$attrName=>$attrNode){
$a[$attrName]=ConvertTypes($attrNode->value);
}
$output['@attributes']=$a;
}
foreach($t=>v的输出){
if(is_数组($v)&&count($v)==1&&$t!='@attributes')){
$output[$t]=$v[0];
}
}
}
打破
}
返回$output;
}
//转换类型
函数类型($org){
如果(是数字($org)){
$val=floatval($org);
}否则{
如果($org=='true'){
$val=true;
}如果($org=='false',则为else){
$val=false;
}否则{
如果($org==''){
$val=null;
}否则{
$val=$org;
}
}
}
返回$val;
}
这是因为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');
}
谢谢你发布这个。虽然我可能更喜欢使用xpath(因为我已经在手头的解析工作中使用它),但我也更喜欢使用JSON对象,所以我将使用您的函数来跳转StartHanks。xpath肯定显示了它的弱点——直觉上,它肯定不是。这让我走了一半。我确实在寻找
节点的值,但现在我无法让xpath找到该节点,可能是因为
.triplesvrl:diagnostic/foo:diag/foo:record/foo:record.01
为我返回值141
为我$parent->xpath('svrl:diagnostic/foo:diag/foo:record/foo:record.01')
,其中,$parent
是重复上下文,返回4个数组(表示record.01的4个外观),每个数组填充一个完全空的SimpleXMLElement
:数组([0]=>SimpleXMLElement对象())
// 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;
}
foreach($errors as $error)
{
$error->registerXPathNamespace('foo', 'example2.com');
$text = $error->xpath('svrl:text');
$record = $error->xpath('svrl:diagnostic/foo:diag/foo:record');
}