2013-11-04 54 views
2

我想从文件数据库中分组记录。我使用XSLT进行转换的选项导出为XML。如何按XSLT中的节点值对XML元素进行分组?

我一直在做一些搜索和阅读其他职位,我不认为他们完全覆盖我想要做的事情。

的XML的摘录:

<?xml version="1.0" encoding="UTF-8"?> 
<!-- This grammar has been deprecated - use FMPXMLRESULT instead --> 
<FMPDSORESULT xmlns="http://www.filemaker.com/fmpdsoresult"> 
    <ERRORCODE>0</ERRORCODE> 
    <DATABASE>Artpostersnbbs.fp7</DATABASE> 
    <LAYOUT /> 
    <ROW MODID="19" RECORDID="11116"> 
     <Art_Type>Poster</Art_Type> 
     <Location>1</Location> 
     <Line1>ELEVATOR 
        MACHINE 
        ROOM 
        107</Line1> 
    </ROW> 
    <ROW MODID="19" RECORDID="11116"> 
     <Art_Type>Poster</Art_Type> 
     <Location>2</Location> 
     <Line1>ELEVATOR 
        MACHINE 
        ROOM 
        107</Line1> 
    </ROW> 
    <ROW MODID="19" RECORDID="11116"> 
     <Art_Type>Poster</Art_Type> 
     <Location>3</Location> 
     <Line1>ELEVATOR 
        MACHINE 
        ROOM 
        107</Line1> 
    </ROW> 
</FMPDSORESULT> 

我想这个组的每两个ART_TYPE和LINE-一致的记录。被分组应该从比赛添加位置到后一个被分成所以它应该是这样的:

<ROW MODID="19" RECORDID="11116"> 
    <Art_Type>Poster</Art_Type> 
    <Location>1 2 3</Location> 
    <Line1>ELEVATOR 
      MACHINE 
      ROOM 
      107 
    </Line1> 
</ROW> 

关于如何开始将不胜感激任何帮助。还有什么好的xslt 1.0测试程序?

在此先感谢!

编辑:我指着Muenchian分组的,发现这个网站:http://www.jenitennison.com/xslt/grouping/muenchian.html

所以从阅读,我想出了:

<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:key name="artTypeNames" match="ROW" use="Art_Type" /> 
    <xsl:key name="artCopy" match="ROW" use="Line1" /> 
    <xsl:template match="FMPDSORESULT"> 
    <xsl:for-each select="ROW[count(. | key('artTypeNames', Art_Type)[1]) = 1]"> 
     <xsl:sort select="Art_Type" /> 
     <xsl:value-of select="Art_Type" /> 
     <xsl:for-each select="key('artTypeNames', Art_Type)"> 
      <xsl:sort select="Art_Type" /> 
      <xsl:value-of select="Art_Type" /> 
     </xsl:for-each> 
    </xsl:for-each> 

    <xsl:apply-templates/> 
    </xsl:template> 
</xsl:stylesheet> 

我进入了XML和XSLT到在线XML转换器我得到'XSLT无效'错误。

这令人沮丧。

EDIT2:随着Tim的帮助,我是能够建立一个适当的XSLT转换:

<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fm="http://www.filemaker.com/fmpdsoresult"> 

<xsl:template match="fm:FMPDSORESULT"> 
    <xsl:apply-templates select="fm:ROW[count(. | key('lineData', fm:Line1)[1]) = 1]"> 
    </xsl:apply-templates> 
</xsl:template> 

<xsl:template match="fm:ROW"> 
    <xsl:copy> 
     <xsl:apply-templates select="fm:Art_Type" /> 
     <fm:Location> 
      <xsl:apply-templates select="key('artTypeNames', fm:Art_Type)/fm:Location" /> 
     </fm:Location> 
     <xsl:apply-templates select="fm:Line1" /> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="fm:Location"> 
    <xsl:if test="position() > 1">-</xsl:if> 
    <xsl:value-of select="." /> 
</xsl:template> 

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

这组Art_Type然后由Line1文本,但现在添加了位置编号像这样:

<ROW xmlns="http://www.filemaker.com/fmpdsoresult"> 
<Art_Type>Poster</Art_Type> 
<fm:Location xmlns:fm="http://www.filemaker.com/fmpdsoresult" xmlns="">1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-25-26-27-28-29-30-31-32-33-34</fm:Location> 
<Line1>CUSTODIAL 
    LOUNGE 

    117A 
</Line1> 
</ROW> 
<ROW xmlns="http://www.filemaker.com/fmpdsoresult"> 
<Art_Type>Poster</Art_Type> 
<fm:Location xmlns:fm="http://www.filemaker.com/fmpdsoresult" xmlns="">1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-25-26-27-28-29-30-31-32-33-34</fm:Location> 
<Line1>STORAGE 
    ROOM 

    117B 
</Line1> 
</ROW> 

如果Line1文本不同,它应该将其添加到另一个组中。

+2

如果你正在寻找一个简单的XSLT在线工具你可以试试这个,http://xslttest.appspot.com/ – Hash

+0

谢谢。它会派上用场,一旦我弄清楚如何使用xslt lol。 – clrx

+0

您可能还想在XSLT中使用google“default namespace”。 –

回答

3

你在评论中提到的一个问题是使用命名空间。在你的XML,有一个命名空间声明:

<FMPDSORESULT xmlns="http://www.filemaker.com/fmpdsoresult"> 

这意味着元素,它下面的所有后代元素,属于该命名空间(除非过深重的低了下去)。但是在XSLT中没有提及任何名称空间,因此XSLT正在查找属于NO名称空间的元素。

您需要在XSLT中声明命名空间,然后确保在尝试引用原始XML中的任何元素时使用命名空间前缀。

<xsl:stylesheet version="1.1" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:fm="http://www.filemaker.com/fmpdsoresult"> 
    <xsl:key name="artTypeNames" match="fm:ROW" use="fm:Art_Type" /> 
    <xsl:template match="fm:FMPDSORESULT"> 

至于你的XSLT样,当我尝试它,我没有得到任何错误,虽然也许你只显示一个片段。分组看起来是正确的(假设您确实打算将元素按Art_Type)分组,但缺少的是复制现有元素的任何代码。

<xsl:for-each select="fm:ROW[count(. | key('artTypeNames', fm:Art_Type)[1]) = 1]"> 
    <xsl:sort select="fm:Art_Type" /> 
    <xsl:copy> 
     <xsl:copy-of select="@*" /> 
     <xsl:copy-of select="fm:Art_Type" /> 

所以,这个片段复制现有ROW元件,它的属性,然后将Art_Type元件(这将是该组中的所有元素是相同的)。

试试这个(完整)XSLT

<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fm="http://www.filemaker.com/fmpdsoresult"> 
    <xsl:key name="artTypeNames" match="fm:ROW" use="fm:Art_Type" /> 

    <xsl:template match="fm:FMPDSORESULT"> 
    <xsl:for-each select="fm:ROW[count(. | key('artTypeNames', fm:Art_Type)[1]) = 1]"> 
     <xsl:sort select="fm:Art_Type" /> 
     <xsl:copy> 
      <xsl:copy-of select="@*" /> 
      <xsl:copy-of select="fm:Art_Type" /> 
      <fm:Location> 
      <xsl:for-each select="key('artTypeNames', fm:Art_Type)"> 
       <xsl:if test="position() > 1">-</xsl:if> 
       <xsl:value-of select="fm:Location" /> 
      </xsl:for-each> 
      </fm:Location> 
      <xsl:copy-of select="fm:Line1" /> 
     </xsl:copy> 
    </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

注意没有必要排序在内部for-each循环,因为很明显Art_Type将是该组中的所有元素是相同的。

编辑:如果你想检查两个领域,以确定什么构成一个组,你需要使用连接的关键来实现这一点。在你的情况下,你说你要检查Art_Typefm:Line1,所以你的钥匙可能看起来像这样。

<xsl:key name="artTypeNames" match="fm:ROW" use="concat(fm:Art_Type, '||', fm:Line1)" /> 

请注意使用'||'这里。这可以是任何事情,只要它不出现在你正在检查的任何元素中。

要使用此键,您只需按照与以前类似的方式使用它,但要使用元素的连接值。例如

<xsl:apply-templates select="fm:ROW[count(. | key('artTypeNames', concat(fm:Art_Type, '||', fm:Line1))[1]) = 1]"> 

注意,它往往是更好地使用XSL:申请模板的代替的xsl:for-每个,如果仅仅是为了减少缩进。

试试这个XSLT太

<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fm="http://www.filemaker.com/fmpdsoresult"> 
    <xsl:output method="xml" indent="yes"/> 
    <xsl:key name="artTypeNames" match="fm:ROW" use="concat(fm:Art_Type, '||', fm:Line1)"/> 

    <xsl:template match="fm:FMPDSORESULT"> 
     <xsl:apply-templates select="fm:ROW[count(. | key('artTypeNames', concat(fm:Art_Type, '||', fm:Line1))[1]) = 1]"> 
     <xsl:sort select="fm:Art_Type"/> 
     </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="fm:ROW"> 
     <xsl:copy> 
     <xsl:apply-templates select="@*"/> 
     <xsl:apply-templates select="fm:Art_Type"/> 
     <fm:Location> 
      <xsl:apply-templates select="key('artTypeNames', concat(fm:Art_Type, '||', fm:Line1))/fm:Location"/> 
     </fm:Location> 
     <xsl:apply-templates select="fm:Line1"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="fm:Location"> 
     <xsl:if test="position() &gt; 1">-</xsl:if> 
     <xsl:value-of select="."/> 
    </xsl:template> 

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

还要注意使用XSLT identity transform的复制现有的元素,而不是XSL:复制的

+0

我在看web上的其他stackoverflow和其他地方的例子,他们的例子没有显示使用命名空间来访问XML元素,所以我想我应该忽略它。我试过了你的XSLT,它可以工作,除非它不测试Art_Type和Line1,只是Art_Type。我应该提到我上面发布的XML只是这个问题的一小部分。 – clrx

+0

我编辑了我的答案,以显示在确定组时如何检查两个元素。 –

+0

工程很好,让我更好地了解XSLT的工作原理。只需要多玩一下。 Liquid XML给了我一个错误,说有多个根元素,我假设因为根元素被剥离了,现在只有一堆根元素。我将如何防止它剥离根元素或读取根元素? – clrx

4

如果您使用的是XSLT 2.0,请在xsl:for-each-group上查找信息。如果您使用的是1.0,请查找关于“Muenchian分组”的信息。

+0

所以我发现这个:http://www.jenitennison.com/xslt/grouping/muenchian.html,它给了Muechian分组的一个很好的运行,但我不确定我是否正确地做到了这一点。 – clrx

相关问题