问题
Given the xml file
<a>
<b>
<d>v</d>
</b>
<c>
<d>v</d>
</c>
</a>
And the xpath "//d/text()"
I want to apply the xpath only to c and not to the whole document.
This has to work with libxml2 2.7.6
Doing this does not work;
xmlNodePtr node = <node pointer to c>
xmlXPathContextPtr xpathCtx = xmlXPathNewContext( node->doc );
xpathCtx->node = node;
xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression ( "//d/text()", xpathCtx);
Returned xpathObj contains refences to both /a/b/d and /a/c/d. Only /a/c/d was expected.
回答1:
Use the path .//d
or .//d/text()
if you want to find descendants relative to another node. A path starting with //d
searches all d
descendant elements of the root node (also called document node).
回答2:
Solved with:
xmlNodePtr node = <node pointer to c>
xmlXPathContextPtr xpathCtx = xmlXPathNewContext( node->doc );
//Update the document to set node as root
xmlNodePtr myParent = node->parent;
xmlNodePtr originalRootElement = xmlDocGetRootElement( node->doc );
xmlDocSetRootElement( node->doc, node );
xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression ( "//d/text()", xpathCtx);
//xpathObj contains only /a/c/d, as expected
//restore the xml document
xmlDocSetRootElement( originalRootElement->doc, originalRootElement );
xmlAddChild( myParent, node );
Tested with valdrind.
The above example gives just the idea. In the following the complete implementation (thanks to Julo for his comment):
//Update Doc to support xpath on logical root node and restore the document structure at scope exit
class XmlDoc_UpdateDocAndRestoreAtScopeExit {
public:
XmlDoc_UpdateDocAndRestoreAtScopeExit( _xmlDoc* doc, _xmlNode* logicalRootNode) :
_doc ( doc ),
_logicalRootNode ( logicalRootNode ),
_originalRootElement( 0 ),
_refParent ( 0 ),
_refPrevSibling ( 0 ),
_refNextSibling ( 0 ),
_toRestore(false)
{
_originalRootElement = xmlDocGetRootElement( doc );
_refParent = _logicalRootNode->parent;
_refPrevSibling = _logicalRootNode->prev;
_refNextSibling = _logicalRootNode->next;
if ( _logicalRootNode != _originalRootElement ) {
xmlDocSetRootElement( _doc, _logicalRootNode );
_toRestore = true;
}
}
~XmlDoc_UpdateDocAndRestoreAtScopeExit() {
if ( _toRestore ) {
//Restore the root node
xmlDocSetRootElement( _doc, _originalRootElement );
//Restore the node at its original place
if ( 0 != _refPrevSibling ) {
xmlAddNextSibling( _refPrevSibling, _logicalRootNode );
} else if ( 0 != _refNextSibling ) {
xmlAddPrevSibling( _refNextSibling, _logicalRootNode );
} else {
xmlAddChild( _refParent, _logicalRootNode );
}
}
}
private:
XmlDoc_UpdateDocAndRestoreAtScopeExit() ; // not implemented
XmlDoc_UpdateDocAndRestoreAtScopeExit(const XmlDoc_UpdateDocAndRestoreAtScopeExit &) ; // not implemented
XmlDoc_UpdateDocAndRestoreAtScopeExit & operator= (const XmlDoc_UpdateDocAndRestoreAtScopeExit &) ; // not implemented
private:
_xmlDoc* _doc;
_xmlNode* _logicalRootNode;
_xmlNode* _originalRootElement;
_xmlNode* _refParent;
_xmlNode* _refPrevSibling;
_xmlNode* _refNextSibling;
bool _toRestore;
};
//Somewhere in the code...
xmlNodePtr node = <node pointer to c>
xmlXPathContextPtr xpathCtx = xmlXPathNewContext( node->doc );
//Here set the _rootNodePtr as the root of the document to use xpath on your "logical" root
//At scope exit the document's structure will be restored.
XmlDoc_UpdateDocAndRestoreAtScopeExit xmlDoc_UpdateDocAndRestoreAtScopeExit( node->doc, node );
xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression ( "//d/text()", xpathCtx);
//xpathObj contains only /a/c/d, as expected
来源:https://stackoverflow.com/questions/33416524/libxml2-xpath-relative-to-sub-node