想要使用XSL 1.0来匹配特定节点中每个第一次出现的值

时间:2013-01-10 00:55:36

标签: xml xslt xslt-1.0

我想匹配特定节点值的第一次出现,我感到难过。

我尝试了<xsl:when test="root/content/contentType = root/content/contentType[.='generic'][1]">,但它与每次出现都匹配,<xsl:when test="root/content/contentType[.='generic'][1]">

也是如此

我想在下面输出HTML。第一个项目有一个标题,但每个项目都有一个相同值的类。

这是我的XML。

非常感谢任何想法。

XML:

<root>
    <content>
        <contentType>ingredients</contentType>
        <listItems>
            <item>Item 1</item>
            <item>Item 2</item>
        </listItems>
    </content>
    <content>
        <contentType>generic</contentType>
        <listItems>
            <item>Item 1</item>
            <item>Item 2</item>
        </listItems>
    </content>
    <content>
        <contentType>generic</contentType>
        <listItems>
            <item>Item 1</item>
            <item>Item 2</item>
        </listItems>
    </content>
    <content>
        <contentType>ingredients</contentType>
        <listItems>
            <item>Item 1</item>
            <item>Item 2</item>
        </listItems>
    </content>
    <content>
        <contentType>directions</contentType>
        <listItems>
            <item>Item 1</item>
            <item>Item 2</item>
        </listItems>
    </content>
</root>

期望的输出:

<div class="ingredients">
    <h2>Ingredients</h2>
    <ul>
        <li>Item 1</li>
        <li>Item 2</li>
    </ul>
</div>
<div class="generic">
    <h2>Generic</h2>
    <ul>
        <li>Item 1</li>
        <li>Item 2</li>
    </ul>
</div>
<div class="generic">
    <ul>
        <li>Item 1</li>
        <li>Item 2</li>
    </ul>
</div>
<div class="ingredients">
    <ul>
        <li>Item 1</li>
        <li>Item 2</li>
    </ul>
</div>
<div class="directions">
    <h2>Directions</h2>
    <ul>
        <li>Item 1</li>
        <li>Item 2</li>
    </ul>
</div>

编辑:

XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:template match="/">

        <xsl:for-each select="root/content">
            <div>
                <xsl:attribute name="class"><xsl:value-of select="./contentType"/></xsl:attribute>

                <xsl:choose>
                    <xsl:when test="./contentType[.='ingredients'][1]">
                        <h2><xsl:value-of select="./contentType"/></h2>
                    </xsl:when>
                    <xsl:when test="./contentType[.='generic'][1]">
                        <h2><xsl:value-of select="./contentType"/></h2>
                    </xsl:when>
                    <xsl:when test="./contentType[.='directions'][1]">
                        <h2><xsl:value-of select="./contentType"/></h2>
                    </xsl:when>
                </xsl:choose>
                <ul>
                    <xsl:for-each select="listItems/item">
                        <li><xsl:value-of select="."/></li>
                    </xsl:for-each>
                </ul>
            </div>
        </xsl:for-each>       

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

2 个答案:

答案 0 :(得分:2)

如果您希望了解更多XSLT的乐趣,那么您可能有兴趣知道您可以使用一种名为“Muenchian Grouping”的技术来解决这个问题。实际上,您可以通过 contentType 元素对内容元素进行分组,并对组中的第一个元素采取特殊操作(即为其提供 h2 标题)

首先,您定义一个代表您的组的键

<xsl:key name="content" match="content" use="contentType"/>

因此,您正在寻找内容元素,并按 contentType 进行排列。

现在有点可怕的部分。您可以检查给定的内容元素是否是 contentType

组中第一个元素
 <xsl:if test="generate-id() = generate-id(key('content', contentType)[1])">

为了解决此问题,key('content', contentType)[1]会返回当前 contentType 键中的第一个内容元素。然后,您需要将其与当前内容元素进行比较,以查看它们是否相同。要执行此操作,请使用 generate-id ,它会返回任何元素的唯一ID。

试试这个XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:output method="xml" indent="yes"/>
   <xsl:key name="content" match="content" use="contentType"/>

   <xsl:template match="root/content">
      <div class="{contentType}">
         <xsl:if test="generate-id() = generate-id(key('content', contentType)[1])">
            <h2>
               <xsl:value-of select="contentType"/>
            </h2>
         </xsl:if>
         <xsl:apply-templates select="listItems"/>
      </div>
   </xsl:template>

   <xsl:template match="listItems">
      <ul>
         <xsl:apply-templates select="item"/>
      </ul>
   </xsl:template>

   <xsl:template match="item">
      <li>
         <xsl:value-of select="."/>
      </li>
   </xsl:template>
</xsl:stylesheet>

对于分组问题,Muenchian Grouping是解决问题的最有效方法。

答案 1 :(得分:0)

这个怎么样:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:template match="/">

        <xsl:for-each select="root/content">
            <div class="{contentType}">
                <xsl:if test="not(preceding::content[contentType = current()/contentType])">
                        <h2><xsl:value-of select="contentType"/></h2>
                </xsl:if>
                <ul>
                    <xsl:for-each select="listItems/item">
                        <li><xsl:value-of select="."/></li>
                    </xsl:for-each>
                </ul>
            </div>
        </xsl:for-each>       

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

这可能更清洁一点:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:template match="content">
            <div class="{contentType}">
                <xsl:if test="not(preceding::content[contentType = current()/contentType])">
                        <h2><xsl:value-of select="contentType"/></h2>
                </xsl:if>
                <ul>
                   <xsl:apply-templates select="listItems" />
                </ul>
            </div>
    </xsl:template>
    <xsl:template match="listItem/item">
          <li><xsl:value-of select="."/></li>
    </xsl:template>
</xsl:stylesheet>

这里的一个不一致是你想要的输出的h2值大写,而在这种情况下它们不会。你知道吗?

我认为好的做法是将类和标题作为源XML中的单独值,但如果你真的想在两个地方使用相同的值,你可以通过这样做来大写第一个字母:

<h2><xsl:value-of select="concat(translate(substring(contentType, 1, 1), 
   'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 
   substring(contentType, 2))"/></h2>