使用XSLT 1.0对相邻项进行分组

时间:2014-02-15 00:26:02

标签: xml xslt

我希望得到一些关于如何使用XSLT 1.0在无序列表中对具有相同名称的相邻元素进行分组的建议。

以下是一些XML示例:

<Article>
  <TextContent>
    <p>lorem</p>
    <VisualContent id="1" />
    <VisualContent id="2" />
    <VisualContent id="3" />
    <p>ipsum</p>
    <VisualContent id="4" />
    <p>dolor</p>
    <VisualContent id="5" />
  </TextContent>
</Article>

这是我想要的输出:

<Article>
  <HtmlContent>
    <p>lorem</p>
    <ul>
      <li><img data-id="1" /></li>
      <li><img data-id="2" /></li>
      <li><img data-id="3" /></li>
    </ul>
    <p>ipsum</p>
    <img data-id="4" />
    <p>dolor</p>
    <img data-id="5" />
  </HtmlContent>
</Article>

不幸的是,XSLT 1.0是对此的严格要求。任何建议都将不胜感激。

3 个答案:

答案 0 :(得分:2)

我已跳过<TextContent>变为<HtmlContent><VisualContent id="n" />变为<img data-id="n" />的部分,因为如果没有这些分心,这个问题就很难了。

我选择的方法会查看前面的第一个兄弟,其名称与当前元素的名称相同。该兄弟的唯一ID是可以将相同名称的相邻元素分组的关键:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:key name="prevByName" match="TextContent/*" use="generate-id(preceding-sibling::*[not(name()=name(current()))][1])" />

<xsl:template match="/">
<Article><TextContent>
    <xsl:for-each select="Article/TextContent/*[generate-id()=generate-id(key('prevByName', generate-id(preceding-sibling::*[not(name()=name(current()))][1]))[1])]">
        <xsl:variable name="myGroup" select="key('prevByName', generate-id(preceding-sibling::*[not(name()=name(current()))][1]))" />
        <xsl:choose>
            <xsl:when test="count($myGroup) > 1">
                <ul>
                    <xsl:for-each select="$myGroup">
                    <li><xsl:copy-of select="."/></li>
                    </xsl:for-each>
                </ul>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy-of select="."/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:for-each>
</TextContent></Article>
</xsl:template>
</xsl:stylesheet>

应用于输入示例时,会产生以下结果:

<?xml version="1.0" encoding="utf-8"?>
<Article>
   <TextContent>
      <p>lorem</p>
      <ul>
         <li>
            <VisualContent id="1"/>
         </li>
         <li>
            <VisualContent id="2"/>
         </li>
         <li>
            <VisualContent id="3"/>
         </li>
      </ul>
      <p>ipsum</p>
      <VisualContent id="4"/>
      <p>dolor</p>
      <VisualContent id="5"/>
   </TextContent>
</Article>

修改 这是一个修改版本,只对相邻的“VisualContent”元素进行分组:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:key name="prevByName" match="TextContent/*" use="generate-id(preceding-sibling::*[not(name()=name(current()))][1])" />

<xsl:template match="/Article/TextContent">
    <Article><TextContent>
        <xsl:apply-templates select="*"/>
    </TextContent></Article>
</xsl:template>

<xsl:template match="TextContent/*[not(self::VisualContent)]">
    <xsl:copy-of select="."/>
</xsl:template>

<xsl:template match="VisualContent[generate-id()=generate-id(key('prevByName', generate-id(preceding-sibling::*[not(name()=name(current()))][1]))[1])]">
<xsl:variable name="myGroup" select="key('prevByName', generate-id(preceding-sibling::*[not(name()=name(current()))][1]))" />
    <xsl:choose>
        <xsl:when test="count($myGroup) > 1">
            <ul>
                <xsl:for-each select="$myGroup">
                <li><xsl:copy-of select="."/></li>
                </xsl:for-each>
            </ul>
        </xsl:when>
        <xsl:otherwise>
            <xsl:copy-of select="."/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

这可能会使用一些精简,但我认为原则很明确,我需要睡一觉......

答案 1 :(得分:0)

我也是XSLT的新手。

这不是您的预期输出,我无法获得<ul>,但看看您是否可以在此基础上构建。我也会尝试。

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" encoding="UTF-8" />
    <xsl:template match="/">
        <Article>
            <HtmlContent>
                <xsl:apply-templates />
            </HtmlContent>
        </Article>
    </xsl:template>

    <xsl:template match="VisualContent">
        <li>
            <xsl:element name="img">
                <xsl:attribute name="date-id"><xsl:number/></xsl:attribute>
            </xsl:element>
        </li>
    </xsl:template>

    <xsl:template match="p">
        <xsl:element name="p">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

这是输出:

<Article>
    <HtmlContent>
        <p>lorem</p>
        <li><img date-id="1" /></li>
        <li><img date-id="2" /></li>
        <li><img date-id="3" /></li>
        <p>ipsum</p>
        <li><img date-id="4" /></li>
        <p>dolor</p>
        <li><img date-id="5" /></li>
    </HtmlContent>
</Article>

答案 2 :(得分:0)

在发现之后,我需要当前版本的xsltproc(1.1.29),我进一步更改了样式表。分组仅适用于TextContent的子项,但我需要分组才能处理任何嵌套级别。

以下是我的意见:

<Article>
    <TextContent>
        <p>lorem</p>
        <VisualContent id="1" />
        <VisualContent id="2" />
        <VisualContent id="3" />
        <p>ipsum</p>
        <VisualContent id="4" />
        <p>dolor</p>
        <p>sit</p>
        <VisualContent id="5" />
    </TextContent>
    <p>amet</p>
    <VisualContent id="6" />
    <VisualContent id="7" />
    <p>consectetur</p>
    <VisualContent id="8" />
</Article>

我使用以下样式表:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

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

    <xsl:key name="prevByName" match="*/*" use="generate-id(preceding-sibling::*[not(name()=name(current()))][1])" />

    <xsl:template match="VisualContent">
        <xsl:if test="generate-id()=generate-id(key('prevByName', generate-id(preceding-sibling::*[not(name()=name(current()))][1]))[1])">
            <xsl:variable name="myGroup" select="key('prevByName', generate-id(preceding-sibling::*[not(name()=name(current()))][1]))" />
            <xsl:choose>
                <xsl:when test="count($myGroup) > 1">
                    <ul>
                        <xsl:for-each select="$myGroup">
                            <li>
                                <xsl:copy-of select="."/>
                            </li>
                        </xsl:for-each>
                    </ul>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:copy-of select="."/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

现在,分组适用于任何元素级别。 : - )

<?xml version="1.0" encoding="utf-8"?>
<Article>
    <TextContent>
        <p>lorem</p>
        <ul><li><VisualContent id="1"/></li><li><VisualContent id="2"/></li><li><VisualContent id="3"/></li></ul>


        <p>ipsum</p>
        <VisualContent id="4"/>
        <p>dolor</p>
        <p>sit</p>
        <VisualContent id="5"/>
    </TextContent>
    <p>amet</p>
    <ul><li><VisualContent id="6"/></li><li><VisualContent id="7"/></li></ul>

    <p>consectetur</p>
    <VisualContent id="8"/>
</Article>