如何仅在元素后跟特定元素时才处理元素

时间:2015-07-31 15:05:25

标签: xml xslt

我有这样的XML:

<?xml version="1.0"?>
<root>
  <mtef>
    <slot>
      <options>0</options>
      <char>
        <typeface>2</typeface>
        <mt_code_value>0x0028</mt_code_value>
      </char>
      <char>
        <typeface>3</typeface>
        <mt_code_value>0x0062</mt_code_value>
      </char>
      <char>
        <typeface>2</typeface>
        <mt_code_value>0x0029</mt_code_value>
      </char>
      <tmpl>
        <selector>tmSUP</selector>
        <template_specific_options>0</template_specific_options>
        <sub/>
        <slot>
          <options>1</options>
        </slot>
        <slot>
          <options>0</options>
          <char>
            <typeface>3</typeface>
            <mt_code_value>0x0063</mt_code_value>
          </char>
          <end/>
        </slot>
        <end/>
      </tmpl>
      <end/>
    </slot>
    <end/>
  </mtef>
</root>

如果带有tmpl选择器的tmSUP元素前面有charmt_code_value为0x0029(右括号的HTML实体),则处理此tmpl元素必须找到左括号,并在其模板中使用中间char元素。

我遇到的问题是char元素的双重处理,首先是他们自己的模板,然后是tmpl[selector='tmSUP']模板。当后续char元素跟随tmpl的{​​{1}}为0x0029时,如何防止char元素被处理?

我的样式表目前看起来像这样:

mt_code_value

这导致此输出:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    <xsl:template match="/">
        <root>
           <xsl:apply-templates select=".//mtef" />
        </root>
    </xsl:template>

    <xsl:template match="mtef">
        <math>
            <xsl:apply-templates select="slot"/>
        </math>
    </xsl:template>

    <xsl:template match="slot">
        <mrow>
            <xsl:apply-templates/>
        </mrow>
    </xsl:template>

    <xsl:template match="tmpl[selector = 'tmSUP']">
        <msup>
        <mrow>
            <xsl:choose>
                <!-- Closing bracket -->
                <xsl:when test="preceding-sibling::char[1]/mt_code_value = '0x0029'">
                    <xsl:for-each select="preceding-sibling::*">
                        <xsl:sort select="position()" data-type="number" order="ascending"/>
                        <xsl:apply-templates select="."/>
                    </xsl:for-each>
                </xsl:when>
            </xsl:choose>            
        </mrow>
            <xsl:apply-templates select="slot[2]"/>
        </msup>
     </xsl:template>

    <xsl:template match="char[typeface = '2']">
        <mn>
            <xsl:text disable-output-escaping="yes">&amp;#</xsl:text>
            <xsl:value-of select="substring(mt_code_value/text(), 2)"/>
            <xsl:text>;</xsl:text>
        </mn>
    </xsl:template>

    <xsl:template match="char[typeface = '3']">
        <mi>
            <xsl:text disable-output-escaping="yes">&amp;#</xsl:text>
            <xsl:value-of select="substring(mt_code_value/text(), 2)"/>
            <xsl:text>;</xsl:text>
        </mi>
    </xsl:template>

    <xsl:template match="*" />

</xsl:stylesheet>

注意一些元素如何出现两次。

所需的输出是:

<?xml version="1.0" encoding="UTF-8"?><root>
  <math>
    <mrow>  
      <mn>&#x0028;</mn>
      <mi>&#x0061;</mi>
      <mo>&#x2212;</mo>
      <mi>&#x0062;</mi>
      <mn>&#x0029;</mn>
      <msup>
        <mrow>
          <mn>&#x0028;</mn><mi>&#x0061;</mi><mo>&#x2212;</mo><mi>&#x0062;</mi><mn>&#x0029;</mn>
        </mrow>
        <mrow>  
          <mi>&#x0063;</mi>
        </mrow>
      </msup>
    </mrow>
  </math>
</root>

解决这种条件&#34;前瞻&#34;的一般方法是什么?使用XSLT进行处理?

1 个答案:

答案 0 :(得分:1)

一种可能性是将tmpl上的条件移出与tmpl本身匹配的模板,并移至与slot匹配的模板中。这样,如果条件为真,那么您只能选择tmpl元素,而不能选择其他char元素。

因此,与slot匹配的模板如下所示:

<xsl:template match="slot">
    <mrow>
        <xsl:choose>
            <xsl:when test="tmpl[selector = 'tmSUP'][preceding-sibling::char[1]/mt_code_value = '0x0029']">
                <xsl:apply-templates select="tmpl" mode="sup" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates/>
            </xsl:otherwise>
        </xsl:choose>
    </mrow>
</xsl:template>

mode上使用xsl:apply-templates只是因为您可能需要在满足条件时使用tmpl的单独模板。

试试这个XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="xml" indent="yes" />

    <xsl:template match="/">
        <root>
           <xsl:apply-templates select=".//mtef" />
        </root>
    </xsl:template>

    <xsl:template match="mtef">
        <math>
            <xsl:apply-templates select="slot"/>
        </math>
    </xsl:template>

    <xsl:template match="slot">
        <mrow>
            <xsl:choose>
                <xsl:when test="tmpl[selector = 'tmSUP'][preceding-sibling::char[1]/mt_code_value = '0x0029']">
                    <xsl:apply-templates select="tmpl" mode="sup" />
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates/>
                </xsl:otherwise>
            </xsl:choose>
        </mrow>
    </xsl:template>

    <xsl:template match="tmpl" mode="sup">
        <msup>
            <mrow>
                <xsl:for-each select="preceding-sibling::*">
                    <xsl:sort select="position()" data-type="number" order="ascending"/>
                    <xsl:apply-templates select="."/>
                </xsl:for-each>        
            </mrow>
            <xsl:apply-templates select="slot[2]"/>
        </msup>
     </xsl:template>

    <xsl:template match="char[typeface = '2']">
        <mn>
            <xsl:text disable-output-escaping="yes">&amp;#</xsl:text>
            <xsl:value-of select="substring(mt_code_value/text(), 2)"/>
            <xsl:text>;</xsl:text>
        </mn>
    </xsl:template>

    <xsl:template match="char[typeface = '3']">
        <mi>
            <xsl:text disable-output-escaping="yes">&amp;#</xsl:text>
            <xsl:value-of select="substring(mt_code_value/text(), 2)"/>
            <xsl:text>;</xsl:text>
        </mi>
    </xsl:template>

    <xsl:template match="*" />
</xsl:stylesheet>

我不确定这是否能为您提供所需的输出,但这可能是一个开始。

当您使用XSLT 2.0时,稍微不同的方法是使用xsl:for-each-group和以tmpl结尾的组。然后,您可以测试组中的最后一个元素是否与条件匹配,并相应地采取相应措施。

尝试这个XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="xml" indent="yes" />

    <xsl:template match="/">
        <root>
           <xsl:apply-templates select=".//mtef" />
        </root>
    </xsl:template>

    <xsl:template match="mtef">
        <math>
            <xsl:apply-templates select="slot"/>
        </math>
    </xsl:template>

    <xsl:template match="slot">
        <mrow>
            <xsl:for-each-group select="*" group-ending-with="tmpl">
                <xsl:variable name="last" select="current-group()[last()]" />
                <xsl:choose>
                    <xsl:when test="$last[self::tmpl][selector = 'tmSUP'][preceding-sibling::char[1]/mt_code_value = '0x0029']">
                        <xsl:apply-templates select="$last" mode="sup" />
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:apply-templates select="current-group()" />
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </mrow>
    </xsl:template>

    <xsl:template match="tmpl" mode="sup">
        <msup>
            <mrow>
                <xsl:for-each select="current-group() except tmpl">
                    <xsl:sort select="position()" data-type="number" order="ascending"/>
                    <xsl:apply-templates select="."/>
                </xsl:for-each>        
            </mrow>
            <xsl:apply-templates select="slot[2]"/>
        </msup>
     </xsl:template>

     <xsl:template match="char[typeface = '2']">
        <mn>
            <xsl:text disable-output-escaping="yes">&amp;#</xsl:text>
            <xsl:value-of select="substring(mt_code_value/text(), 2)"/>
            <xsl:text>;</xsl:text>
        </mn>
    </xsl:template>

    <xsl:template match="char[typeface = '3']">
        <mi>
            <xsl:text disable-output-escaping="yes">&amp;#</xsl:text>
            <xsl:value-of select="substring(mt_code_value/text(), 2)"/>
            <xsl:text>;</xsl:text>
        </mi>
    </xsl:template>

    <xsl:template match="*" />
</xsl:stylesheet>