为啥我的 XPath 表达式在 XML 文档中找不到新添加的节点?

Posted

技术标签:

【中文标题】为啥我的 XPath 表达式在 XML 文档中找不到新添加的节点?【英文标题】:Why doesn't my XPath expression find a newly added node in an XML document?为什么我的 XPath 表达式在 XML 文档中找不到新添加的节点? 【发布时间】:2021-11-30 13:48:17 【问题描述】:

我尝试向 XML (svg) 文档添加一个新节点,但是当我随后尝试使用 XPath 表达式查询它时,它找不到新节点。

use strict;
use warnings;
use XML::LibXML;
use XML::LibXML::XPathContext;

my $parser = XML::LibXML->new();
my $svg = $parser->load_xml(string => <<'SVG');
<svg xmlns="http://www.w3.org/2000/svg" 
     xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
<g inkscape:groupmode="layer" id="old-id" inkscape:label="old-label">...</g>
</svg>
SVG

my $layer = XML::LibXML::Element->new('g');     # same result with 'svg:g'
$layer->setAttribute('inkscape:groupmode', 'layer');
$layer->setAttribute('id', 'new-id');
$layer->setAttribute('inkscape:label', 'new-label');
$svg->documentElement()->appendChild($layer);

print "Dump:\n$svg\n";

print "Xpath:\n";
my $xpc = XML::LibXML::XPathContext->new($svg);
$xpc->registerNs('svg', 'http://www.w3.org/2000/svg');
my $xpath = '//svg:g';
foreach my $node ($xpc->findnodes($xpath)) 
    print $node->getAttribute('id'), ": ", $node->getAttribute('inkscape:label'), "\n";

打印出来:

Dump:
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
<g inkscape:groupmode="layer" id="old-id" inkscape:label="old-label">initially</g>
<g inkscape:groupmode="layer" inkscape:label="new-label" id="new-id"/></svg>

Xpath:
old-id: old-label

当我转储整个 xml 文档时会显示新节点,但 XPath 表达式只报告旧节点。

为什么会这样?如何让 XPath 表达式也找到新添加的节点?

【问题讨论】:

也许你需要像这样创建元素:$root = $dom-&gt;documentElement(); $dom-&gt;setDocumentElement( $root ); $element = $dom-&gt;createElement( $nodename );,然后追加。该元素是在文档中创建的,但尚未分配到任何地方。来自here 【参考方案1】:

虽然您的 print 语句正在输出 XML,如果重新解析该 XML 将给您正确的结果,但您的内存中 DOM 不知道新元素的命名空间。要告诉新元素有关命名空间的信息,我们可以使用 SetNamespaceSetAttributeNS 每个 docs

use strict;
use warnings;
use XML::LibXML;
use XML::LibXML::XPathContext;

my $parser = XML::LibXML->new();
my $svg = $parser->load_xml(string => <<'SVG');
<svg xmlns="http://www.w3.org/2000/svg" 
     xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
<g inkscape:groupmode="layer" id="old-id" inkscape:label="old-label">...</g>
</svg>
SVG

my $layer = XML::LibXML::Element->new('g');     # same result with 'svg:g'
$layer->setNamespace('http://www.w3.org/2000/svg');
$layer->setNamespace('http://www.inkscape.org/namespaces/inkscape', 'inkscape', 0);
$layer->setAttributeNS('http://www.inkscape.org/namespaces/inkscape', 'groupmode', 'layer');
$layer->setAttribute('id', 'new-id');
$layer->setAttributeNS('http://www.inkscape.org/namespaces/inkscape', 'label', 'new-label');
$svg->documentElement()->appendChild($layer);

print "Dump:\n$svg\n";

print "Xpath:\n";
my $xpc = XML::LibXML::XPathContext->new($svg);
$xpc->registerNs('svg', 'http://www.w3.org/2000/svg');
my $xpath = '//svg:g';
foreach my $node ($xpc->findnodes($xpath)) 
    print $node->getAttribute('id'), ": ", $node->getAttribute('inkscape:label'), "\n";

输出

Dump:
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
<g inkscape:groupmode="layer" id="old-id" inkscape:label="old-label">...</g>
<g inkscape:groupmode="layer" id="new-id" inkscape:label="new-label"/></svg>

Xpath:
old-id: old-label
new-id: new-label

【讨论】:

以上是关于为啥我的 XPath 表达式在 XML 文档中找不到新添加的节点?的主要内容,如果未能解决你的问题,请参考以下文章

Xpath语法

流式 XPath 评估

如何使用 XPath 和 Java 更新 XML

xpath语法

xPath 用法总结整理

xPath 用法总结整理