libxml2:相对于子节点的xpath

时间:2015-10-29 14:08:51

标签: xpath libxml2

给出xml文件

<a>
   <b>
      <d>v</d>
   </b>
   <c>
      <d>v</d>
   </c>
</a>

xpath&#34; // d / text()&#34;

我想仅将xpath应用于c而不是整个文档。

这必须与libxml2 2.7.6

一起使用

这样做不起作用;

xmlNodePtr node = <node pointer to c>
xmlXPathContextPtr xpathCtx = xmlXPathNewContext( node->doc );
xpathCtx->node = node;

xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression ( "//d/text()", xpathCtx);

返回的xpathObj包含对/ a / b / d和/ a / c / d的重新声明。预计只有/ a / c / d。

2 个答案:

答案 0 :(得分:1)

如果要查找相对于另一个节点的后代,请使用路径.//d.//d/text()。以//d开头的路径将搜索根节点(也称为文档节点)的所有d后代元素。

答案 1 :(得分:1)

解决:

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 );

使用valdrind测试。

上面的例子给出了这个想法。在下面的完整实现中(感谢Julo的评论):

//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