使用XSLT在XML中删除重复项

5

我需要在以下XML中删除重复项:

<ListOfRowIDWithListOfBooks xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/">
  <RowIDWithListOfBooks>
    <Row_ID>ADOA-XssK</Row_ID>
    <ListOfBookInfo>
      <book>
        <BookType>Brand</BookType>
        <BookName>jon</BookName>
      </book>
      <book>
        <BookType>Brand</BookType>
        <BookName>jon</BookName>
      </book>
    </ListOfBookInfo>
  </RowIDWithListOfBooks>
</ListOfRowIDWithListOfBooks>

有人能帮忙吗?

4个回答

6

使用标准分组解决方案可以轻松完成此任务。不要使用单个选择语句来执行此操作,因为已知会导致性能问题。

注意:有关identity.xsl的引用只需要在样式表中包含众所周知的身份转换模板即可。

[XSLT 1.0]

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

    <xsl:key name="k-books" match="book" use="concat(BookType,'|',BookName)"/>

    <xsl:include href="identity.xsl"/>

    <xsl:template match="ListOfBookInfo">
        <ListOfBookInfo>
            <xsl:copy>
                <xsl:apply-templates select="book
                [generate-id()
                =generate-id(key('k-books',concat(BookType,'|',BookName))[1])]"/>
            </xsl:copy>
        </ListOfBookInfo>
    </xsl:template>

</xsl:stylesheet>

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

    <xsl:include href="identity.xsl"/>

    <xsl:template match="ListOfBookInfo">
        <ListOfBookInfo>
            <xsl:for-each-group select="book" 
                group-by="concat(BookType,'|',BookName)">
                <xsl:apply-templates select="."/>
            </xsl:for-each-group>
        </ListOfBookInfo>
    </xsl:template>

</xsl:stylesheet>

你应该用实际的恒等转换替换你的 xsl:include。这样,即使对于不知道恒等转换是什么的人,你的样式表也能正常工作。 - Daniel Haley
@DevNull,这正是xsl:include的作用;-)我已经附上了一条带有参考文献的注释。感谢您的反馈。 - Emiliano Poggi

3

试试这个XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/">
    <xsl:apply-templates select="//ListOfBookInfo/book[not(BookType = preceding-sibling::book/BookType
                         and BookName = preceding-sibling::book/BookName)]"/>
  </xsl:template>

  <xsl:template match="book">
    <xsl:copy-of select="."/>
  </xsl:template>

</xsl:stylesheet>

它将选择具有唯一的 BookType 和 BookName 的书籍。在您的示例中,结果应为:
<book xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/">
        <BookType>Brand</BookType>
        <BookName>jon</BookName>
      </book>

0
如果您对如何使用Muenchian Grouping实现此操作感兴趣,那么您首先需要定义一个“键”来识别行内重复的书籍。这是XSLT中常用的技术之一。
<xsl:key 
   name="books"
   match="book"
   use="concat(concat(../../Row_ID, '#'), concat(concat(BookType, '#'), BookName))" />

我使用连接键RowIDBookTypeBookName来实现这一点。该键将包含一个书籍列表,所有这些书籍都具有该特定键值。请注意,使用#字符作为连接字符。如果您的XML中有任何可能出现#的情况,您需要选择另一个字符(或字符串)。

现在,当您匹配book元素时,可以像这样检查重复项

<xsl:variable 
  name="lookup"
  select="concat(concat(../../Row_ID, '#'), concat(concat(BookType, '#'), BookName))" />
<xsl:if test="generate-id() = generate-id(key('books', $lookup)[1])">

换句话说,这个元素是我们关键字中的第一个元素吗?
以下是完整的XSLT。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:output method="xml" indent="yes"/>
   <xsl:key 
      name="books"
      match="book"
      use="concat(concat(../../Row_ID, '#'), concat(concat(BookType, '#'), BookName))"/>

   <xsl:template match="book">
      <xsl:variable name="lookup" select="concat(concat(../../Row_ID, '#'), concat(concat(BookType, '#'), BookName))"/>
      <xsl:if test="generate-id() = generate-id(key('books', $lookup)[1])">
         <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
         </xsl:copy>
      </xsl:if>
   </xsl:template>

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

还要注意使用身份转换,以便可以复制其他节点而无需显式引用它们。当将此XSLT应用于您的输入时,将生成以下输出:

<RowIDWithListOfBooks xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/">
   <Row_ID>ADOA-XssK</Row_ID>
   <ListOfBookInfo>
      <book>
         <BookType>Brand</BookType>
         <BookName>jon</BookName>
      </book>
   </ListOfBookInfo>
</RowIDWithListOfBooks>

编辑:我已经修改了XSLT,删除了一个不必要的模板匹配。


0

我需要最终结果看起来像这样,但我就是不明白 :(<ListOfRowIDWithListOfBooks xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/"> <RowIDWithListOfBooks> <Row_ID>ADOA-XssK</Row_ID> <ListOfBookInfo> <book> <BookType>品牌</BookType> <BookName>Jon</BookName> </book>
</ListOfBookInfo>
</RowIDWithListOfBooks>
- Steph
我认为分组不会做我需要的事情:>( 一切都在选择中 --- 无论我怎么做,最终问题在于不知道该如何处理选择。 - Steph
说实话,如果你被困在 XSLT 1.0 中,最好先通过 XML 解析器运行它。 - hoodaticus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接