XSLT 1.0和标记化属性

时间:2011-12-15 05:28:41

标签: xslt xslt-1.0 tokenize

[edit]发布更新后的修订版XML和所需的输出,详见Vincent Biragnet的回答和评论。

我正在尝试将一些代码放在一起,将XML数据转换为基于文本的元文件。我没有运气让它吐出所需的输出而且目前有点卡住了,所以任何帮助都会受到赞赏。

XSLT 1.0不会使标记化变得容易,而且这就是我陷入困境的地方:我想将@syn视为CSV字符串并在需要时将其拆分。

我正在使用以下XML数据(请注意,此XML文件中除< Meta>节点外的所有节点都可以有任何名称。)

<Meta>
    <Subject>
        <People>
            <Jane_Doe syn="janie, jd" />
            <John_Doe/>
        </People>
        <Object>
            <Table>
                <Leg/>
            </Table>
            <Chair syn="seat" />
        </Object>
    </Subject>
    <Test1>
        <Test2 syn="testy"/>
        <Test3>
            <Test4/>
        </Test3>
    </Test1>
</Meta>

需要转换此XML,以便输出看起来像这样:

[Subject]
    [People]
        Jane_Doe
            {janie}
            {jd}
        John_Doe
    [Object]
        [Table]
            Leg
        Chair
            {seat}
[Test1]
    Test2
        {testy}
    [Test3]
        Test4

我目前的XSL:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text" indent="yes"/>
    <xsl:template match="/Meta"><xsl:apply-templates/></xsl:template>
    <xsl:template match="child::*"><xsl:call-template name="master"/><xsl:apply-templates/></xsl:template>
    <xsl:template name="master">
        <xsl:choose>
            <xsl:when test="count(child::*) = 0">
                <xsl:value-of select="local-name()"/>
                <xsl:apply-templates select="@syn"/>
            </xsl:when>
            <xsl:otherwise>
                [<xsl:value-of select="local-name()"/>]
                <xsl:apply-templates select="@syn"/>
        </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="tokenize" match="@syn">
        <xsl:param name="text" select="."/>
        <xsl:param name="separator" select="','"/>
        <xsl:value-of select="$text"/>
        <xsl:choose>
            <xsl:when test="not(contains($text, $separator))">
                {<xsl:value-of select="normalize-space($text)"/>}
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="normalize-space(substring-before($text, $separator))"/>
                <xsl:call-template name="tokenize">
                    {<xsl:with-param name="text" select="substring-after($text, $separator)"/>}
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

3 个答案:

答案 0 :(得分:0)

使用XSLT 1.0准确获取输出有点棘手,但您可以使用您尝试过的机制。这是一个使用缩进输出的XSLT 1.0:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="Meta">
        <xsl:apply-templates/>
    </xsl:template>
    <xsl:template match="*">
        <xsl:param name="depth" select="0"/>
        <xsl:choose>
            <xsl:when test="$depth > 0">
                <xsl:text>&#10;</xsl:text>            
                <xsl:call-template name="addSpace">
                    <xsl:with-param name="nb" select="$depth"></xsl:with-param>
                </xsl:call-template>                
            </xsl:when>
            <xsl:when test="position() > 1">
                <xsl:text>&#10;</xsl:text>                            
            </xsl:when>
        </xsl:choose>        
        <xsl:choose>
            <xsl:when test="count(*) > 0">
                <xsl:text>[</xsl:text>
                <xsl:value-of select="local-name()"/>
                <xsl:text>]</xsl:text>                
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="local-name()"/>
            </xsl:otherwise>
        </xsl:choose>
        <xsl:apply-templates select="@syn|*">
            <xsl:with-param name="depth" select="$depth + 1"/>
        </xsl:apply-templates>       
    </xsl:template>
    <xsl:template name="tokenize" match="@syn">
        <xsl:param name="text" select="."/>
        <xsl:param name="depth" select="1"/>
        <xsl:param name="separator" select="','"/>
        <xsl:text>&#10;</xsl:text>
        <xsl:call-template name="addSpace">
            <xsl:with-param name="nb" select="$depth"></xsl:with-param>
        </xsl:call-template>
        <xsl:choose>
            <xsl:when test="not(contains($text, $separator))">
                <xsl:text>{</xsl:text>
                <xsl:value-of select="normalize-space($text)"/>
                <xsl:text>}</xsl:text>
            </xsl:when>
            <xsl:otherwise>
                <xsl:text>{</xsl:text>
                <xsl:value-of select="normalize-space(substring-before($text, $separator))"/>
                <xsl:text>}</xsl:text>
                <xsl:call-template name="tokenize">
                    <xsl:with-param name="text" select="substring-after($text, $separator)"/>
                    <xsl:with-param name="separator" select="$separator"/>
                    <xsl:with-param name="depth" select="$depth"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="addSpace">
        <xsl:param name="nb"/>
        <xsl:text> </xsl:text>
        <xsl:if test="$nb >1 ">
            <xsl:call-template name="addSpace">
                <xsl:with-param name="nb" select="$nb - 1"></xsl:with-param>
            </xsl:call-template>            
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

您输入的结果是:

[Subject]
 [People]
  Jane_Doe
   {janie}
   {jd}
  John_Doe
 [Object]
  [Table]
   Leg
  Chair
   {seat}

请注意与输出的区别:table元素位于括号之间,因为它有一个或多个子元素。

答案 1 :(得分:0)

你几乎就在那里,只是错误的东西括号导致错误!

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text" indent="yes"/>
    <xsl:template match="/Meta"><xsl:apply-templates/></xsl:template>
    <xsl:template match="child::*"><xsl:call-template name="master"/><xsl:apply-templates/></xsl:template>
    <xsl:template name="master">
        <xsl:choose>
            <xsl:when test="count(child::*) = 0">
                <xsl:value-of select="local-name()"/>
                <xsl:apply-templates select="@syn"/>
            </xsl:when>
            <xsl:otherwise>
                [<xsl:value-of select="local-name()"/>]
                <xsl:apply-templates select="@syn"/>
        </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="tokenize" match="@syn">
        <xsl:param name="text" select="."/>
        <xsl:param name="separator" select="','"/>        
        <xsl:choose>
            <xsl:when test="not(contains($text, $separator))">
                {<xsl:value-of select="normalize-space($text)"/>}
            </xsl:when>
            <xsl:otherwise>
                {<xsl:value-of select="normalize-space(substring-before($text, $separator))"/>}
                <xsl:call-template name="tokenize">
                    <xsl:with-param name="text" select="substring-after($text, $separator)"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

答案 2 :(得分:0)

我会这样做:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

  <xsl:output method="text"/>

  <xsl:param name="lf" select="'&#10;'"/>
  <xsl:param name="start-indent" select="'    '"/>
  <xsl:param name="br1" select="'['"/>
  <xsl:param name="br2" select="']'"/>
  <xsl:param name="br3" select="'{'"/>
  <xsl:param name="br4" select="'}'"/>
  <xsl:param name="sep" select="','"/>

  <xsl:template match="/Meta" priority="10">
    <xsl:apply-templates select="*">
      <xsl:with-param name="indent" select="''"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="*[not(*)]">
    <xsl:param name="indent"/>
    <xsl:value-of select="concat($indent, local-name(), $lf)"/>
    <xsl:apply-templates select="@syn">
      <xsl:with-param name="indent" select="concat($indent, $start-indent)"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="*[*]">
    <xsl:param name="indent"/>
    <xsl:value-of select="concat($indent, $br1, local-name(), $br2, $lf)"/>
    <xsl:apply-templates select="*">
      <xsl:with-param name="indent" select="concat($indent, $start-indent)"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="@syn">
    <xsl:param name="indent"/>
    <xsl:param name="text" select="."/>
    <xsl:choose>
      <xsl:when test="not(contains($text, $sep))">
        <xsl:if test="normalize-space($text)">
          <xsl:value-of select="concat($indent, $br3, normalize-space($text), $br4, $lf)"/>
        </xsl:if>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="concat($indent, $br3, normalize-space(substring-before($text, $sep)), $br4, $lf)"/>
        <xsl:apply-templates select=".">
          <xsl:with-param name="indent" select="$indent"/>
          <xsl:with-param name="text" select="substring-after($text, $sep)"/>
        </xsl:apply-templates>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>