2013-02-12 128 views
2

我正在处理由四个步骤组成的XSL转换。我想出了各个步骤,但我一直在坚持如何把它们放在一起。这是我想做的事:带有标记化和不同值的复杂XSLT转换

源XML文档:

<application> 
    <contactPerson> 
     <name>Dominik</name> 
     <countryCode>DE,SP</countryCode> 
    </contactPerson> 
    <contactPerson> 
     <name>Andrea</name> 
     <countryCode>FR</countryCode> 
    </contactPerson> 
    <contactPerson> 
     <name>Alex</name> 
     <countryCode>FR,DE</countryCode> 
    </contactPerson> 
    <contactPerson> 
     <name>Andre</name> 
     <countryCode>FR</countryCode> 
    </contactPerson> 
</application> 

目标XML文档:

<application> 
    <memberState> 
     <countryCode>DE</countryCode> 
     <contactPerson> 
      <name>Dominik</name> 
     </contactPerson> 
     <contactPerson> 
      <name>Dorothea</name> 
     </contactPerson> 
    </memberState> 
    <memberState> 
     <countryCode>FR</countryCode> 
     <contactPerson> 
      <name>Fiona</name> 
     </contactPerson> 
     <contactPerson> 
      <name>Fabian</name> 
     </contactPerson> 
     <contactPerson> 
      <name>Florian<name> 
     </contactPerson> 
    </memberState> 
    <memberState> 
     <countryCode>GB</countryCode> 
     <contactPerson> 
      <name>Gabi</name> 
     </contactPerson> 
     <contactPerson> 
      <name>Gert</name> 
     </contactPerson> 
    </memberState> 
</application> 

我确定了过程中的以下步骤:

  1. COUNTRYCODE标签都有效,在逗号分割的值,并把它们放在一个列表
  2. 列表
  3. 删除双occurances列表中的
  4. 对于每一个新的每个值创建一个新的COUNTRYCODE -node COUNTRYCODE -node,添加与联系人该国

现在所有的人,我想通了,该怎么办第1步

<memberState> 
    <countryCodes> 
     <xsl:for-each select="/application/contactPerson"> 
      <xsl:for-each select="tokenize(./countryCode, ',')"> 
       <countryCode> 
        <xsl:value-of select="."/> 
       </countryCode> 
      </xsl:for-each> 
     </xsl:for-each> 
    </countryCodes> 
</memberState> 

对于第2步我可以使用distinct-values()

对于第3步 + 第4步我实现了以下解决方案:

<xsl:for-each select="/application/contactPerson/countryCode[not(. = ../preceding-sibling::*/countryCode)]"> 
    <memberState> 
     <countryCode> 
      <xsl:value-of select="."/> 
     </countryCode> 
     <xsl:for-each select="/application/contactPerson[countryCode = current()]"> 
      <contactPerson> 
       <name> 
        <xsl:value-of select="name"/> 
       </name> 
      </contactPerson> 
     </xsl:for-each> 
    </memberState> 
</xsl:for-each> 

但我怎么可以把一切融合在一起?我的想法是将每个步骤的输出保存在一个变量中,并在下一步中使用它,但是我遇到了XSLT中的变量为只读的问题。有没有办法以某种方式连接单个解决方案来获得所需的结果?

+0

从您的描述来看,您似乎希望有一种样式表可以执行某种形式的魔术;给出由Dominik,Andrea,Alex和Andre组成的人员名单,它必须删除有关Andrea,Alex和Andre的信息,并添加有关Dorothea,Fiona,Fabian,Florian,Gabi和Gert的信息。凉! – 2013-02-12 17:16:03

+0

你是对的 - 现在你提到它。我必须在代码发布之前对代码进行一些修改,并忘记更改数据。对不起,这是我的错。幸运的是,Martin Honnen能够弄清楚我的意思:)。谢谢你提及 - 下次我会更加小心。 – 2013-02-13 09:11:31

回答

7

我只想提出一个单一的步骤for-each-group解决方案:

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

<xsl:output indent="yes"/> 
<xsl:strip-space elements="*"/> 

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

<xsl:template match="application"> 
    <xsl:copy> 
    <xsl:for-each-group select="contactPerson" group-by="tokenize(countryCode, ',')"> 
     <memberState> 
     <countryCode><xsl:value-of select="current-grouping-key()"/></countryCode> 
     <xsl:apply-templates select="current-group()"/> 
     </memberState> 
    </xsl:for-each-group> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="contactPerson/countryCode"/> 

</xsl:stylesheet> 

当然还有一些改造措施是可能的,但与像for-each-group是XSLT 2.0提供还是先看看使用这些,而不是使用几种变换工具脚步。

如果你想使用distinct-values它当然是可能的;然而,我只会将字符串值存储在一个变量中并对其进行操作,我不明白为什么使用XSLT 2.0时您需要临时树。因此,这里是使用distinct-values样本和存储他们在第二个步骤进行处理(我使用效率的关键)的变量:

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

<xsl:output indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:variable name="countryCodes" select="distinct-values(application/contactPerson/countryCode/tokenize(., ','))"/> 

<xsl:variable name="main-input" select="/"/> 

<xsl:key name="country" match="contactPerson" use="tokenize(countryCode, ',')"/> 

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

<xsl:template match="application"> 
    <xsl:copy> 
    <xsl:for-each select="$countryCodes"> 
     <memberState> 
     <countryCode><xsl:value-of select="."/></countryCode> 
     <xsl:apply-templates select="key('country', ., $main-input)"/> 
     </memberState> 
    </xsl:for-each> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="contactPerson/countryCode"/> 

</xsl:stylesheet> 

我想,但是我的第一个建议是比较容易给出XSLT 2.0的支持。

+0

“每个组”都是我一直在寻找的。我只介绍了不同的步骤,因为我试图逐步解决问题。由于支持XSLT 2.0,“for-each-group”解决方案要好得多。谢谢你的回答。 – 2013-02-12 16:02:12

+0

+1,用于很好的分组代码。 – 2014-11-14 10:24:47