libxml2: xpath relative to sub node

孤人 提交于 2019-12-02 08:03:04

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!