2011-04-06 48 views
3

我已经将XML文档构造几个XML元素迭代如下XSLT 1.0到超过与逗号分隔的值

<items> 
<item> 
    <name>item1</name> 
    <attributes>a,b,c,d</attributes> 
</item> 
<item> 
    <name>item2</name> 
    <attributes>c,d,e</attributes> 
</item> 
</items> 

对于每个唯一的属性值(以逗号分隔)我需要列出与该相关联的所有项名称值如下所示:

a : item1 
b : item1 
c : item1, item2 
d : item1, item2 
e : item2 

我的最初的计划是使用模板来解析属性入属性节点,围绕每个用适当的标记,然后用XPATH表达式等

分离出唯一值
Attribute[not(.=following::Attribute)] 

但由于模板的结果不是经历XML解析器的节点集,我无法遍历它。我也尝试过exslt的node-set()函数,仅仅意识到它不允许我遍历各个Attribute节点。

在这一点上,我很难找到一个简单的方法来做到这一点,真的很感激任何帮助或想法如何进行。谢谢!

+0

好问题,+1。查看我的答案以获得完整的解决方案和解释。 – 2011-04-06 02:43:51

+0

我喜欢思考这个问题 – 2011-04-06 03:06:27

回答

1

这种转变

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
xmlns:ext="http://exslt.org/common"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:key name="kAtrByVal" match="attr" use="."/> 

<xsl:template match="/"> 
    <xsl:variable name="vrtfPass1"> 
    <groups> 
    <xsl:apply-templates/> 
    </groups> 
    </xsl:variable> 

    <xsl:variable name="vPass1" 
     select="ext:node-set($vrtfPass1)"/> 

    <xsl:apply-templates select="$vPass1/*"/> 
</xsl:template> 

<xsl:template match="item"> 
    <group name="{name}"> 
    <xsl:apply-templates select="attributes"/> 
    </group> 
</xsl:template> 

<xsl:template match="attributes" name="tokenize"> 
    <xsl:param name="pText" select="."/> 

    <xsl:if test="string-length($pText)"> 
    <xsl:variable name="vText" select= 
     "concat($pText,',')"/> 
    <attr> 
    <xsl:value-of select="substring-before($vText,',')"/> 
    </attr> 
    <xsl:call-template name="tokenize"> 
    <xsl:with-param name="pText" select= 
    "substring-after($pText,',')"/> 
    </xsl:call-template> 
    </xsl:if> 
</xsl:template> 

<xsl:template match= 
    "attr[generate-id() 
     = 
     generate-id(key('kAtrByVal',.)[1]) 
     ] 
    "> 
    <xsl:value-of select="concat('&#xA;',.,': ')"/> 

    <xsl:for-each select="key('kAtrByVal',.)"> 
    <xsl:value-of select="../@name"/> 
    <xsl:if test="not(position()=last())"> 
    <xsl:text>, </xsl:text> 
    </xsl:if> 
    </xsl:for-each> 
</xsl:template> 

<xsl:template match="text()"/> 
</xsl:stylesheet> 

时所提供的XML文档应用:

<items> 
    <item> 
     <name>item1</name> 
     <attributes>a,b,c,d</attributes> 
    </item> 
    <item> 
     <name>item2</name> 
     <attributes>c,d,e</attributes> 
    </item> 
</items> 

产生想要的,正确的结果:

a: item1 
b: item1 
c: item1, item2 
d: item1, item2 
e: item2 

说明

  1. 1步:标记化和最终结果是:

<groups> 
    <group name="item1"> 
    <attr>a</attr> 
    <attr>b</attr> 
    <attr>c</attr> 
    <attr>d</attr> 
    </group> 
    <group name="item2"> 
    <attr>c</attr> 
    <attr>d</attr> 
    <attr>e</attr> 
    </group> 
</groups> 

0.2。Pass2将Pass1的结果(使用扩展功能ext:node-set()转换为节点集)作为输入,执行Muenchian分组并生成最终的所需结果。

+0

这件事仍然有点麻烦。是否使用了',这样当'被调用时,Muenchian分组模板可以做到这一点吗? – ratherOCD 2011-04-06 16:00:59

+0

@ratherOCD:不,这个模板简单地覆盖了文本节点的XSLT内置模板,并确保文本节点不会被输出为的结果 – 2011-04-06 16:08:17

+0

谢谢,我得到了一切工作。有没有一种按字母顺序排列attr的好方法? – ratherOCD 2011-04-06 16:33:30

0

我的第一个想法是做两遍。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="/"> 
     <items> 
      <xsl:apply-templates/> 
     </items> 
    </xsl:template> 
    <xsl:template match="item"> 
     <item name="{name}"> 
      <xsl:apply-templates select="attributes"/> 
     </item> 
    </xsl:template> 
    <xsl:template match="attributes" name="tokenize"> 
     <xsl:param name="text" select="."/> 
     <xsl:param name="separator" select="','"/> 
     <xsl:choose> 
      <xsl:when test="not(contains($text, $separator))"> 
       <val> 
        <xsl:value-of select="normalize-space($text)"/> 
       </val> 
      </xsl:when> 
      <xsl:otherwise> 
       <val> 
        <xsl:value-of select="normalize-space(
         substring-before($text, $separator))"/> 
       </val> 
       <xsl:call-template name="tokenize"> 
        <xsl:with-param name="text" select="substring-after(
            $text, $separator)"/> 
       </xsl:call-template> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 

哪个生产::

<items> 
    <item name="item1"> 
     <val>a</val> 
     <val>b</val> 
     <val>c</val> 
     <val>d</val> 
    </item> 
    <item name="item2"> 
     <val>c</val> 
     <val>d</val> 
     <val>e</val> 
    </item> 
</items> 

然后应用以下样式表到输出:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:strip-space elements="*"/> 
    <xsl:key name="byVal" match="val" use="." /> 
    <xsl:template match="val[generate-id() = 
          generate-id(key('byVal', .)[1])]"> 
     <xsl:value-of select="." /> 
     <xsl:text> : </xsl:text> 
     <xsl:apply-templates select="key('byVal', .)" mode="group" /> 
     <xsl:text>&#10;</xsl:text> 
    </xsl:template> 
    <xsl:template match="val" mode="group"> 
     <xsl:value-of select="../@name" /> 
     <xsl:if test="position() != last()"> 
      <xsl:text>, </xsl:text> 
     </xsl:if> 
    </xsl:template> 
    <xsl:template match="val" /> 
</xsl:stylesheet> 
首先,使用(略微)修改的 @Alejandro's answer to this previous question版本标记化的 attributes元件

生产时间:

a : item1 
b : item1 
c : item1, item2 
d : item1, item2 
e : item2 

在一个样式表中做这件事需要更多的思考(或扩展功能)。

+0

+1从概念上来说是正确的。 – 2011-04-06 17:47:29