如何在不使用ID的情况下对不同元素进行分组,删除重复项并在xslt中添加组ID?

时间:2019-02-17 11:32:31

标签: xml xslt duplicates grouping

我想根据它们在列表中的位置将新元素内的两个元素重新组合,删除这些对的重复项,按字母顺序对它们进行排序,并为每个组赋予数字ID(使用XSLT 2.0或1.0)。我是XSLT的新手,目前对解决问题的方法一无所知。

在平坦的地标XML列表(来自数据库)中,有地标名称列表和坐标列表。需要重新排列它们,以便每个名称和坐标在新的地标元素内正确分组在一起。位置1的名称需要与第一个coord元素配对,依此类推。

在过去三天中,我在stackoverflow中查找了有关重新分组,重复删除,muenchian分组的主题,并尝试了已发布的示例,但无法将其应用于我的案例。

简化的输入文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<landmarklist>
  <citylist id="1">
    <landmark type="city">
      <name>London</name>
    </landmark>
    <landmark>
      <name>Tower Bridge</name>
      <name>Big Ben</name>
      <name>St Paul's Cathedral</name>
      <name>Big Ben</name>
      <coord>51°30′20″N 0°04′31″W</coord>
      <coord>51° 30′ 3″ N, 0° 7′ 28″ W</coord>
      <coord>51° 30′ 49″ N, 0° 5′ 53″ W</coord>
      <coord>51° 30′ 3″ N, 0° 7′ 28″ W</coord>
    </landmark>
  </citylist>
  <citylist id="2">
    <landmark type="city">
      <name>Paris</name>
    </landmark>
    <landmark>
      <name>Eiffel Tower</name>
      <name>Arc de Triomphe</name>
      <name>Louvre</name>
      <coord>48° 51′ 29.6″ N, 2° 17′ 40.2″ E</coord>
      <coord>48° 52′ 25.68″ N, 2° 17′ 42″ E</coord>
      <coord>48° 51′ 40″ N, 2° 20′ 11″ E</coord>
    </landmark>
  </citylist>
  <citylist id="3">
    <landmark type="city">
      <name>Madrid</name>
    </landmark>
    <landmark>
      <name>Plaza Mayor</name>
      <name>Almudena Cathedral</name>
      <coord>40° 24′ 55.31″ N, 3° 42′ 26.63″ W</coord>
      <coord>40° 24′ 56.11″ N, 3° 42′ 52.41″ W</coord>
    </landmark>
  </citylist>
</landmarklist>

生成的输出文件应如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<landmarklist>
<citylist id="1">
  <landmark type="city">
    <name>London</name>
  </landmark>
  <landmark id="1">
    <name>Big Ben</name>
    <coord>51° 30′ 3″ N, 0° 7′ 28″ W</coord>
  </landmark>
  <landmark id="2">
    <name>St Paul's Cathedral</name>
    <coord>51° 30′ 49″ N, 0° 5′ 53″ W</coord>
  </landmark>
  <landmark id="3">
    <name>Tower Bridge</name>
    <coord>51°30′20″N 0°04′31″W</coord>
  </landmark>
</citylist>
 <citylist id="2">
   <landmark type="city">
     <name>Paris</name>
   </landmark>
   <landmark>
     <name id="4">Arc de Triomphe</name>
     <coord>48° 52′ 25.68″ N, 2° 17′ 42″ E</coord>
   </landmark>
   <landmark>
     <name id="5">Eiffel Tower</name>
     <coord>48° 51′ 29.6″ N, 2° 17′ 40.2″ E</coord>
   </landmark>
   <landmark id="6">
     <name>Louvre</name>
     <coord>48° 51′ 40″ N, 2° 20′ 11″ E</coord>
   </landmark>
 </citylist>
  <citylist id="3">
    <landmark type="city">
      <name>Madrid</name>
    </landmark>
    <landmark id="7">
      <name>Plaza Mayor</name>
      <coord>40° 24′ 55.31″ N, 3° 42′ 26.63″ W</coord>     
    </landmark>
    <landmark id="8">
      <name>Almudena Cathedral</name>
      <coord>40° 24′ 56.11″ N, 3° 42′ 52.41″ W</coord>
    </landmark>
  </citylist>
</landmarklist>

为了使用键来对名称和coord元素进行分组,我使用了一个单独的转换来添加ID(并且我临时重命名了城市名称,以便不计入ID)。还是可以在自己的转换方案中管理一切?

  <landmark>
     <name id="1">Tower Bridge</name>
     <name id="2">Big Ben</name>
     <name id="3">St Paul's Cathedral</name>
     <name id="4">Big Ben</name>
     <coord id="1">51°30′20″N 0°04′31″W</coord>
     <coord id="2">51° 30′ 3″ N, 0° 7′ 28″ W</coord>
     <coord id="3">51° 30′ 49″ N, 0° 5′ 53″ W</coord>
     <coord id="4">51° 30′ 3″ N, 0° 7′ 28″ W</coord>
  </landmark>

但是我现在如何使用两个不同元素的ID重新组合?据我了解,我发现的示例始终使用一个示例?我对自己的工作原理缺乏了解,感到非常抱歉。

任何帮助将不胜感激。非常感谢。

1 个答案:

答案 0 :(得分:0)

您可以在一次通过中进行分组和排序,第二次使用xsl:number作为id属性,以下是XSLT 3(与Saxon 9.8或更高版本或Altova 2017或更高版本兼容):< / p>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    expand-text="yes"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:mode name="group" on-no-match="shallow-copy"/>

  <xsl:output method="xml" indent="yes"/>

  <xsl:variable name="first-pass">
      <xsl:apply-templates mode="group"/>
  </xsl:variable>

  <xsl:template match="/">
      <xsl:apply-templates select="$first-pass/node()"/>
  </xsl:template>

  <xsl:template match="citylist/landmark[not(@type)]" mode="group">
      <xsl:variable name="coords" select="coord"/>
      <xsl:for-each-group select="name" composite="yes" group-by="., let $p := position() return $coords[$p]">
          <xsl:sort select="current-grouping-key()[1]"/>
          <landmark>
              <xsl:copy-of select="."/>
              <coord>{current-grouping-key()[2]}</coord>
          </landmark>
      </xsl:for-each-group>
  </xsl:template>

  <xsl:template match="citylist/landmark[not(@type)]">
      <xsl:copy>
          <xsl:attribute name="id">
              <xsl:number level="any" count="landmark[not(@type)]"/>
          </xsl:attribute>
          <xsl:apply-templates/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/pPzifp8/1

如果需要,当然可以适应XSLT 2:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="2.0">

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

  <xsl:output method="xml" indent="yes"/>

  <xsl:variable name="first-pass">
      <xsl:apply-templates mode="group"/>
  </xsl:variable>

  <xsl:template match="/">
      <xsl:apply-templates select="$first-pass/node()"/>
  </xsl:template>

  <xsl:template match="citylist/landmark[not(@type)]" mode="group">
      <xsl:variable name="coords" select="coord"/>
      <xsl:for-each-group select="name" group-by="concat(., '|', for $p in position() return $coords[$p])">
          <xsl:sort select="."/>
          <landmark>
              <xsl:variable name="p" select="position()"/>
              <xsl:copy-of select="., $coords[$p]"/>
          </landmark>
      </xsl:for-each-group>
  </xsl:template>

  <xsl:template match="citylist/landmark[not(@type)]">
      <xsl:copy>
          <xsl:attribute name="id">
              <xsl:number level="any" count="landmark[not(@type)]"/>
          </xsl:attribute>
          <xsl:apply-templates/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

http://xsltransform.net/gVAjbSZ