鉴于深度嵌套的XML树,我想找到一个特定的元素。在那一点上,我想将X包装在一个与更高元素处于同一级别的新元素中。然后,我想继续使用“特定”元素后面的原始树的其余部分。
例如,给定此输入:
<root>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</branch>
</root>
我想在&lt; ul&gt;中找到第2项(十分简单)。我想在第2项之前插入一个新的分支级元素。然后我想继续第2项(继续祖先节点)。也就是说,我想要这个输出:
<root>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 1</li>
</ul>
</div>
</branch>
<branch>
<div>
<p>New branch here</p>
</div>
</branch>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</branch>
</root>
为了制作通用解决方案,我遇到了一个难题。我认为它将涉及多个模式或键以及祖先节点的处理以查找节点名称和属性。任何帮助表示赞赏。
好的,这是我到目前为止所拥有的。它部分工作(例如,我复制一些节点,但没有属性;我也复制了太多的节点,但我认为这是一个开始)。我的想法是我需要一个递归函数。我从我关注的最远的祖先(分支)开始,并且对于每个子节点,其最终后代是特定元素(li“item 2”),我复制每个节点及其属性,将其附加到前一个节点(即newNewTree是什么)。我递归直到达到某个元素(li的第2项),此时我返回NewNewTree变量,我的意图是它是一个右祖先元素的树(没有文本)。为了完成这项工作,我认为我需要做的是通过身份模板覆盖来处理函数中的每个节点,该覆盖复制节点及其属性。但今晚太累了,无法尝试。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="http://www.example.com"
exclude-result-prefixes="xs my"
version="2.0">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:sequence select="$origNodesNoText" />
</xsl:template>
<xsl:variable name="ancestorElemName" select="'div'" />
<xsl:variable name="origNodesNoText">
<xsl:apply-templates select="/" mode="origNodesNoText"/>
</xsl:variable>
<xsl:template match="text()" mode="origNodesNoText"/>
<xsl:template match="li[.='Item 2']" mode="origNodesNoText">
<xsl:variable name="newTree">
<xsl:element name="{local-name(ancestor::*[local-name()=$ancestorElemName][1])}">
<xsl:for-each select="ancestor::*[local-name()=$ancestorElemName][1]/attribute::*">
<xsl:attribute name="{local-name()}" select="."/>
</xsl:for-each>
</xsl:element>
</xsl:variable>
<xsl:sequence select="my:split(ancestor::*[local-name()=$ancestorElemName][1],.,$newTree)" />
</xsl:template>
<xsl:function name="my:split">
<xsl:param name="ancestorElemNode" />
<xsl:param name="callingNode" />
<xsl:param name="newTree" />
<xsl:message>Calling my split</xsl:message>
<xsl:choose>
<xsl:when test="$ancestorElemNode ne $callingNode">
<xsl:message>Found a node to copy its name and attributes</xsl:message>
<xsl:variable name="newNewTree">
<xsl:for-each select="$newTree/node()">
<xsl:copy /> <!-- doesn't copy attribute nodes so not what we want -->
</xsl:for-each>
<xsl:element name="{local-name($ancestorElemNode)}">
<xsl:for-each select="$ancestorElemNode/attribute::*">
<xsl:attribute name="{local-name()}" select="."/>
</xsl:for-each>
</xsl:element>
</xsl:variable>
<xsl:sequence select="my:split($ancestorElemNode/child::*[descendant::*[. eq $callingNode]][1],$callingNode,$newNewTree)" />
</xsl:when>
<xsl:otherwise>
<xsl:message>Found the end point</xsl:message>
<xsl:sequence select="$newTree" />
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</xsl:stylesheet>
答案 0 :(得分:0)
您希望以下列方式处理输入的节点。首先,让我们为经常需要引用的节点定义两个术语:
现在我们可以根据它们与输出树的对应关系在输入树中定义几个类节点。
branch
)。我用近乎身份的变换做到这一点。我们需要对香蕉及其后代进行特殊处理。其中一些需要复制两次,有些只需复制一次,但最简单的做法就是两次处理整个子树。
整个样式表可能如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="Bullseye" select="//li[.='Item 2']"/>
<!--* Default identity transform *-->
<xsl:template match="comment()">
<xsl:comment><xsl:value-of select="."/></xsl:comment>
</xsl:template>
<xsl:template match="processing-instruction()">
<xsl:variable name="pitarget" select="name()"/>
<xsl:processing-instruction name="{$pitarget}">
<xsl:value-of select="."/>
</xsl:processing-instruction>
</xsl:template>
<xsl:template match="@*|*">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!--* Banana *-->
<xsl:template match="branch[descendant::* intersect $Bullseye]">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="pre-bullseye"/>
</xsl:copy>
<xsl:call-template name="insertion"/>
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="post-bullseye"/>
</xsl:copy>
</xsl:template>
<!--* first pass over subtree rooted at Banana *-->
<xsl:template match="comment()" mode="pre-bullseye">
<xsl:apply-templates select="."/>
</xsl:template>
<xsl:template match="processing-instruction()" mode="pre-bullseye">
<xsl:apply-templates select="."/>
</xsl:template>
<xsl:template match="@*|*|text()" mode="pre-bullseye">
<xsl:choose>
<xsl:when test="descendant::* intersect $Bullseye">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()" mode="pre-bullseye"/>
</xsl:copy>
</xsl:when>
<xsl:when test=". << $Bullseye">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:when>
<xsl:when test=". is $Bullseye"/>
<xsl:when test=". >> $Bullseye"/>
<xsl:otherwise>
<xsl:message terminate="yes"
>Unexpected case in mode pre-bullseye, dying now.</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--* Second pass over subtree rooted at Banana *-->
<xsl:template match="comment()" mode="post-bullseye">
<xsl:apply-templates select="."/>
</xsl:template>
<xsl:template match="processing-instruction()" mode="post-bullseye">
<xsl:apply-templates select="."/>
</xsl:template>
<xsl:template match="@*|*|text()" mode="post-bullseye">
<xsl:choose>
<xsl:when test="descendant::* intersect $Bullseye">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()" mode="post-bullseye"/>
</xsl:copy>
</xsl:when>
<xsl:when test=". << $Bullseye"/>
<xsl:when test=". is $Bullseye">
<xsl:copy-of select="."/>
</xsl:when>
<xsl:when test=". >> $Bullseye">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes"
>Unexpected case in post-bullseye, dying now.</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="insertion">
<branch>
<div>
<p>New branch here.</p>
</div>
</branch>
</xsl:template>
</xsl:stylesheet>
在您提供的输入上运行时,此样式表会生成以下输出。
<?xml version="1.0" encoding="UTF-8"?>
<root>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 1</li>
</ul>
</div>
</branch>
<branch>
<div>
<p>New branch here.</p>
</div>
</branch>
<branch>
<div stuff="no">
<ul>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</branch>
</root>
答案 1 :(得分:0)
这是另一种选择。基本上你:
target
参数)。branch
)并应用该元素的模板。 (如果有多个具有该值的元素,则在第一次出现时分支。)/*/*
。如果根元素有多个子元素,则此样式表需要进行一些调整。branch
),因此我们知道这是插入新分支的地方。branch
参数)以及以下兄弟。XML输入
<root>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</branch>
</root>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="target" select="'Item 2'"/>
<xsl:key name="branch" match="*[.=$target]" use="."/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*|(//*[key('branch',.)])[1]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="precID" select="for $id in preceding-sibling::* return generate-id($id)"/>
<xsl:variable name="follID" select="for $id in (self::*,following-sibling::*) return generate-id($id)"/>
<xsl:apply-templates select="/*/*" mode="ident">
<xsl:with-param name="restricted2IDs" select="$precID" tunnel="yes"/>
</xsl:apply-templates>
<xsl:apply-templates select="/*/*" mode="ident">
<xsl:with-param name="restricted2IDs" select="generate-id(current())" tunnel="yes"/>
<xsl:with-param name="branch" select="';-)'" tunnel="yes"/>
</xsl:apply-templates>
<xsl:apply-templates select="/*/*" mode="ident">
<xsl:with-param name="restricted2IDs" select="$follID" tunnel="yes"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="@*|node()" mode="ident">
<xsl:param name="restricted2IDs" tunnel="yes"/>
<xsl:param name="branch" tunnel="yes"/>
<xsl:choose>
<xsl:when test="*[generate-id()=$restricted2IDs]">
<xsl:choose>
<xsl:when test="$branch">
<p>New branch here</p>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="@*|node()[generate-id()=$restricted2IDs]" mode="ident"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="descendant::*[generate-id()=$restricted2IDs]">
<xsl:copy>
<!--If you want the attributes in the new branch, remove the "if" and just use "@*|node".-->
<xsl:apply-templates select="if ($branch) then node() else @*|node()" mode="ident"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="ident"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
XML输出
<root>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 1</li>
</ul>
</div>
</branch>
<branch>
<div>
<p>New branch here</p>
</div>
</branch>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</branch>
</root>