XSLT按分隔符划分文本,并为其指定元素和属性编号

时间:2013-07-30 13:12:56

标签: xml xslt

我的文字有经文编号。

现在,我想用文字数字分开文本,并将单个数字作为ID给出。

由于我不知道如何从源代码中获取数字,我只是给了它们连续的数字,但如果有可能的话,我希望它们从源中分配它们的实际数字。因此,如果缺少一节经文,则XSLT不会连续计数,而是跳过一个数字。

但除此之外,我遇到了问题,我在开头就得到一个空元素<l n="1"/>

我认为我的XSLT在某种程度上匹配<p>,因此实际的n =“1”变为n =“2”。

我该如何解决?

我的来源:

<root>
<p>1 This is 2 a <hi rend="bold">beautiful</hi> example 3 poem 4 for showing! 5 my problem</p> 
</root>

转换为:

<xsl:stylesheet version="2.0" exclude-result-prefixes="xs"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="@* |node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="root/p">
        <p>
        <xsl:variable name="words" select="tokenize(text(),'(1|2|3|4|5|6|7|8|9|0)')" as="xs:string*"/>
        <xsl:for-each select="1 to xs:integer(floor(count($words) div 1))">
            <xsl:variable name="vIndex" select="(.)" as="xs:integer"/>
            <l><xsl:attribute name="n"
            select="position()"/>
                <xsl:value-of select="$words[$vIndex]"/>
            </l>
        </xsl:for-each>
        </p>
    </xsl:template>
</xsl:stylesheet>

我得到的是:

<root>
    <p>
      <l n="1"/>
      <l n="2"> This is </l>
      <l n="3"> a beautiful example </l>
      <l n="4"> poem </l>
      <l n="5"> for showing </l>
      <l n="6"> my problem</l>
   </p>
</root>

想要的输出是:

<root>
    <p>
      <l n="1"> This is </l>
      <l n="2"> a <hi rend="bold">beautiful</hi> example </l>
      <l n="3"> poem </l>
      <l n="4"> for showing! </l>
      <l n="5"> my problem</l>
   </p>
</root>

编辑:我在我的示例中添加了一个元素。

2 个答案:

答案 0 :(得分:0)

tokenize返回的第一个元素将出现在第一个数字之前(在您的情况下,是一个空字符串)。因此,假设您只想要在第一个数字后面显示的内容,则必须从tokenize的结果中删除第一个元素(例如,使用remove函数)。

试试这个:

<xsl:stylesheet version="2.0" exclude-result-prefixes="xs"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="@* |node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="root/p">
        <p>
        <xsl:variable name="tokens" select="tokenize(text(),'(1|2|3|4|5|6|7|8|9|0)')" as="xs:string*"/>
        <xsl:variable name="words" select="remove($tokens, 1)" as="xs:string*"/>
        <xsl:for-each select="1 to xs:integer(floor(count($words) div 1))">
            <xsl:variable name="vIndex" select="(.)" as="xs:integer"/>
            <l><xsl:attribute name="n"
            select="position()"/>
                <xsl:value-of select="$words[$vIndex]"/>
            </l>
        </xsl:for-each>
        </p>
    </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:0)

使用analyze-string代替tokenize

<xsl:stylesheet version="2.0" exclude-result-prefixes="xs"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="@* |node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="root/p">
        <p>
          <xsl:analyze-string select="." regex="([0-9]+)([^0-9]*)">
            <xsl:matching-substring>
              <l name="{regex-group(1)}">
                <xsl:value-of select="regex-group(2)"/>
              </l>
            </xsl:matching-substring>
        </xsl:analyze-string>
     </p>

    </xsl:template>
</xsl:stylesheet>

如果您希望p元素具有元素子元素,则需要更复杂的方法:

<xsl:stylesheet version="2.0" exclude-result-prefixes="xs"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="node() | @*" mode="#all">
        <xsl:copy>
            <xsl:apply-templates select="@* |node()" mode="#current"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="root/p">
        <p>
          <xsl:variable name="children">
            <xsl:apply-templates select="node()" mode="wrap-number"/>
          </xsl:variable>
          <xsl:for-each-group select="$children/node()" group-starting-with="n">
            <l n="{.}">
              <xsl:apply-templates select="current-group() except ."/>
            </l>
          </xsl:for-each-group>
        </p>
    </xsl:template>

    <xsl:template match="p//text()" mode="wrap-number">
      <xsl:analyze-string select="." regex="[0-9]+">
        <xsl:matching-substring>
          <n><xsl:value-of select="."/></n>
        </xsl:matching-substring>
        <xsl:non-matching-substring>
          <xsl:value-of select="."/>
        </xsl:non-matching-substring>
      </xsl:analyze-string>
    </xsl:template>

</xsl:stylesheet>

但这只有在确保行的数字包含在p元素的文本节点子元素中时才有效,如果在元素子元素中也允许它们(例如<p><span>1 This </span>2 is an example.</p>)那么更多的工作需要。