代码之家  ›  专栏  ›  技术社区  ›  badperson

xslt:从父元素中的混合内容创建新节点

  •  0
  • badperson  · 技术社区  · 15 年前

    我正在尝试编写一个模板,它将获取父元素中文本节点和元素的混合,并创建一个新的节点。我做了很多搜索,找不到我要找的东西……所以希望我不会问基本的问题。

    下面是我要转换的XML示例:

    <?xml version="1.0"?>
    <root>
     <para>Here is some text that will ask users to enter a <rule-line/> [<emph type="it">date</emph>], and maybe their <rule-line/> [<emph type="it">name</emph>]. The text could come in different [<emph type="it">order</emph>] <rule-line/>, and their could be any number of instances.</para>
    
    </root>
    

    我想把加括号的文本和规则分组成这样的新元素:

    <entry>[<emph type"it">date</emph>]</entry>
    

    我有一个模板,可以识别我想要更改的文本,我可以更改它,但我不知道如何将我想要的文本添加到结果树中并忽略旧文本。

    以下是相关模板:

    <xsl:template match="para">
     <xsl:for-each select="* | text()">
      <xsl:choose>
    
       <xsl:when test="self::rule-line and following-sibling::node()[1][starts-with(., ' [')] and string(node-name(following-sibling::node()[2])) = 'emph' and following-sibling::node()[3][starts-with(., ']')]">
        <xsl:comment>made match</xsl:comment>
        <xsl:call-template name="codeEntry">
         <xsl:with-param name="rule" select="."/>
         <xsl:with-param name="openBracket" select="following-sibling::node()[1]"/>
         <xsl:with-param name="emphTag" select="following-sibling::node()[2]"/>
         <xsl:with-param name="closeBracketString" select="following-sibling::node()[3]"/>
        </xsl:call-template>
       </xsl:when>
       <xsl:otherwise>
        <xsl:copy-of select="."/>
       </xsl:otherwise>
      </xsl:choose>
     </xsl:for-each>
    
    </xsl:template>
    
    
    <xsl:template name="codeEntry">
    <xsl:param name="rule"/>
    <xsl:param name="openBracket"/>
    <xsl:param name="emphTag"/>
    <xsl:param name="closeBracketString"/>
    <entry>
    <xsl:copy-of select="$openBracket"/>
    <xsl:copy-of select="$emphTag"/>
    <xsl:text>] </xsl:text>
    </entry>
    <xsl:value-of select="substring-after($closeBracketString, ']')"/>
    
    </xsl:template>
    

    显然,when语句获取一组节点,但是当每个节点通过另外的块时,它将被复制到结果树。我真的不知道如何处理这个问题,因为para可以以任何顺序包含任意数量的这些节点分组,或者没有。(一旦我了解了这一点,我将添加另一个when块来处理规则前的括号文本)

    我认为创建一个告诉模板忽略节点的变量是可行的……但是我对不可变变量及其作用域有点模糊……

    我还试图想出一种方法,我可以尝试递归地这样做……但这需要在一个点上添加一个开始标记,在另一个点上添加一个结束标记,或者如果正在处理的节点在序列的中间,则不添加标记……我知道这在XSLT中会变得很奇怪。

    以前有人遇到过这种情况吗? 谢谢, 杰森

    任何想法

    1 回复  |  直到 15 年前
        1
  •  1
  •   user357812    15 年前

    只是为了好玩(真是一团糟的图式!),此样式表:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="node()|@*" name="identity">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="rule-line"/>
        <xsl:template match="emph">
            <entry>
                <xsl:text>[</xsl:text>
                <xsl:call-template name="identity"/>
                <xsl:text>]</xsl:text>
            </entry>
        </xsl:template>
        <xsl:template match="text()[normalize-space()='[']
                                   [following-sibling::*[1][self::emph]] |
                             text()[normalize-space()=']']
                                   [preceding-sibling::*[1][self::emph]]"
                      priority="1"/>
        <xsl:template match="text()[starts-with(normalize-space(),']')]
                                   [preceding-sibling::*[1][self::emph]]">
            <xsl:value-of select="substring-after(.,']')"/>
        </xsl:template>
        <xsl:template match="text()[substring(normalize-space(),
                                              string-length(normalize-space()),
                                              1) = '[']
                                   [following-sibling::*[1][self::emph]]">
            <xsl:call-template name="crop-both">
                <xsl:with-param name="pString" select="concat(']',.)"/>
            </xsl:call-template>
        </xsl:template>
        <xsl:template match="text()[starts-with(normalize-space(),']')]
                                   [substring(normalize-space(),
                                              string-length(normalize-space()),
                                              1) = '[']
                                   [preceding-sibling::*[1][self::emph]]
                                   [following-sibling::*[1][self::emph]]"
                      priority="1" name="crop-both">
            <xsl:param name="pString" select="."/>
            <xsl:variable name="vReverse">
                <xsl:call-template name="reverse">
                    <xsl:with-param name="pString" 
                                    select="substring-after(.,']')"/>
                </xsl:call-template>
            </xsl:variable>
            <xsl:call-template name="reverse">
                <xsl:with-param name="pString"
                                select="substring-after($vReverse,'[')"/>
            </xsl:call-template>
        </xsl:template>
        <xsl:template name="reverse">
            <xsl:param name="pString"/>
            <xsl:if test="$pString!=''">
                <xsl:call-template name="reverse">
                    <xsl:with-param name="pString"
                                    select="substring($pString,2)"/>
                </xsl:call-template>
                <xsl:value-of select="substring($pString,1,1)"/>
            </xsl:if>
        </xsl:template>
    </xsl:stylesheet>
    

    输出:

    <root>
    <para>Here is some text that will ask users to enter a <entry>[<emph type="it">date</emph>]</entry>, and maybe their <entry>[<emph type="it">name</emph>]</entry>. The text could come in different <entry>[<emph type="it">order</emph>]</entry>, and their could be any number of instances.</para>
    </root>