如何在 XSLT 中按节点值对 XML 元素进行分组? [英] How to group XML elements by node value in XSLT?

查看:39
本文介绍了如何在 XSLT 中按节点值对 XML 元素进行分组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试对 filemaker 数据库中的记录进行分组.我以 XML 格式导出,可选择使用 XSLT 进行转换.

I'm trying to group records from a filemaker database. I'm exporting as XML with the option of using XSLT to transform.

我一直在搜索和阅读其他帖子,但我认为它们并没有完全涵盖我想要做的事情.

I've been doing some search and reading the other posts, and I don't think they cover exactly what I want to do.

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 和 LINE1 的每条记录分组.分组后,它应该将匹配中的位置添加到被分组的位置,因此它应该看起来像:

I want the group each record that matches both ART_TYPE and LINE1. After being grouped it should add the Location from the match to the one being grouped into so it should look like:

<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测试程序?

Any help on how to get started would be appreciated. Also is there any good xslt 1.0 testing program?

提前致谢!

我被指向 muenchian 分组并找到了这个网站:http://www.jenitennison.com/xslt/grouping/muenchian.html

I was pointed to muenchian grouping and found this site: http://www.jenitennison.com/xslt/grouping/muenchian.html

所以从阅读中我想出了:

So from reading that I came up with:

<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 无效"错误.

I entered the XML and the XSLT into an online XML Transformer and I get 'XSLT is invalid' error.

这令人沮丧.

在 Tim 的帮助下,我能够构建正确的 XSLT 转换:

With Tim's help I was able to construct a proper XSLT transform:

<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 文本分组,但现在将位置编号添加到所有这些文本中,如下所示:

It groups the Art_Type and then by the Line1 text, but now adds the location numbers to all of them like this:

<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 文本不同,则应将其添加到另一个组中.

If the Line1 text is different, it should be adding it into another group.

推荐答案

您遇到的一个问题(在评论中提到)与命名空间有关.在您的 XML 中,有一个命名空间声明:

One problem you have, that was mentioned in the comments, is to do with namespaces. In your XML, there is a namespace declaration:

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

这意味着该元素以及它下面的所有后代元素都属于该命名空间(除非被向下覆盖).但是在您的 XSLT 中没有提及任何名称空间,因此 XSLT 正在寻找属于 NO 名称空间的元素.

This means that element, and all descendants elements below it, belong to that namespace (unless over-ridden lower down). But in your XSLT there is no mention of any namespace, and so the XSLT is looking for elements that belong to NO namespace.

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

You will need to declare the namespace in your XSLT, and then ensure you use the namespace prefix when trying to reference any element in the original 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_TypeROW 元素进行分组),但您缺少的是复制现有元素的任何代码.

As for your XSLT sample, I don't get any error when I try it, although perhaps you are only showing a snippet. The grouping looks correct (assuming you are indeed intending to group ROW elements by Art_Type), but what you are missing is any code to copy the existing elements across.

<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 元素(对于组中的所有元素都相同).

So, this snippet copies the existing ROW element, its attributes, and then the Art_Type element (which will be the same for all elements in the group).

试试这个(完整的)XSLT

Try this (complete) 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 对于组中的所有元素都是相同的.

Note there was no need for the sort in the inner for-each loop because obviously Art_Type will be the same for all elements in the group.

如果您想检查两个字段以确定组成一个组的内容,您需要使用连接键来实现这一点.在您的情况下,您说您想同时检查 Art_Typefm:Line1,因此您的密钥可能看起来像这样.

If you wanted to check two fields to determine what makes up a group, you need to use a concatenated key to achieve this. In your case, you say you want to check both Art_Type and fm:Line1, so your key could look something like this.

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

注意'||'的使用这里.这可以是任何东西,只要它不出现在您检查的任何一个元素中即可.

Note the use of the '||' here. This can be anything, just as long as it does not appear in either of the elements you are checking.

要使用此键,您只需以与以前类似的方式使用它,但使用元素的串联值.例如

To use this key, you just use it in a similar manner as before, but with the concatenated values of the elements. For example

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

注意,如果只是为了减少缩进,通常最好使用 xsl:apply-templates 而不是 xsl:for-each.

Note, it is often better to use xsl:apply-templates instead of xsl:for-each, if only to reduce indentation.

也试试这个 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 身份转换来复制现有元素而不是 xsl:copy-of

Also note the use of the XSLT identity transform to copy existing elements instead of xsl:copy-of

这篇关于如何在 XSLT 中按节点值对 XML 元素进行分组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆