可以为LibXML / LibXSLT创建自定义函数来改变上下文吗?

时间:2014-10-02 13:15:55

标签: xslt libxml2 libxslt

我想知道是否有任何方法可以创建与<xsl:for-each/>类似的自定义函数/元素

我知道有一种方法如何register function/element,但是没有一种能够(据我所知)改变上下文并递归执行内部XSLT指令。

例如我想要实现的目标是:

<myxsl:change-context name='x'>
  <xsl:value-of select='name()'/>
</myxsl:change-context>

2 个答案:

答案 0 :(得分:1)

可以通过调入传递给转换函数的上下文对象来修改XPath上下文:

void transformFunction(xsltTransformContextPtr ctxt, 
                       xmlNodePtr node, 
                       xmlNodePtr inst, 
                       xsltElemPreCompPtr comp)
{
    xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;

    /* Save old context */
    xmlNodePtr oldNode = ctxt->node;
    int        oldSize = xpctxt->contextSize;
    int        oldPos  = xpctxt->proximityPosition;

    /* Set up your new context... */

    ctxt->node                = newNode;
    xpctxt->contextSize       = newSize;
    xpctxt->proximityPosition = newPos;

    /*
     * Do something under new context, probably using
     * xsltApplySequenceConstructor...
     */

    /* Restore old context */
    ctxt->node                = oldNode;
    xpctxt->contextSize       = oldSize;
    xpctxt->proximityPosition = oldPos;
}

您还可能需要保存和恢复其他一些上下文变量。有关 libxslt 如何在内部实现for-each,请查看xsltForEach in libxslt/transform.c

答案 1 :(得分:0)

根据@nwellnholf建议,我检查了transform.c,过了一段时间,我想出了如何实现它。

@nwellnholf写道,核心魔法在xsltApplySequenceConstructor内。为了能够使用它,编辑库和公开此函数是必要的,因为最初这个函数只在transform.c中定义。为此,请在transform.h中定义它并重新编译libxslt。

XSLTPUBFUN void xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, xmlNodePtr list, xsltTemplatePtr templ);

第二步是实现自己的xslt函数,继续执行自己的指令并将处理返回给xslt。这些步骤通过以下命令完成:

void elemChangeContext(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst, xsltElemPreCompPtr /*comp*/)
{
    if ( ctxt == NULL || node == NULL || inst == NULL || ctxt->insert == NULL )
        return;

    xmlNodePtr cur = /* change context to different node */;
    xmlNodePtr curInst = inst->children; //sub xslt instruction
    xsltApplySequenceConstructor(ctxt,cur, curInst,NULL);
}