2013-09-19 6 views
2

Я только начал изучать XSLT, и у меня проблемы с игнорированием дублированных элементов.XSLT Игнорировать повторяющиеся элементы

Я искал переполнение стека и видел, как люди задают похожие вопросы. Я попробовал небольшой пример, чтобы увидеть, где я ошибся в своем файле, и смог игнорировать дублированные элементы. Однако проблема для меня, похоже, возникает, когда у меня есть более одного типа элемента.

Для примера:

File1.xml

<?xml-stylesheet type="text/xsl" href="merge2.xsl"?> 

<Main> 
    <Records> 
     <Record> 
      <Description>A</Description> 
     </Record> 
     <Record> 
      <Description>A</Description> 
     </Record> 
     <Record> 
      <Description>B</Description> 
     </Record> 
     <Record> 
      <Description>C</Description> 
     </Record> 
    </Records> 
</Main> 

merge2.xsl

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/Transform"> 
<xsl:output method="html"/> 
<xsl:template match="/"> 
    <table border="1"> 
     <tr> 
      <th>Type</th> 
      <th>Count</th> 
     </tr> 
     <xsl:for-each select="Main/Records/Record"> 
      <xsl:if test ="not(preceding-sibling::Record(Description/text() = current()/Description/text()])"> 
       <tr> 
        <td><xsl:value-of select="Description"/></td> 
        <td><xsl:value-of select="count(//Record[Description/text()=current()/Description/text()])"/></td> 
       </tr> 
      </xsl:if> 
     </xsl:for-each> 
    </table> 
</xsl:template> 
</xsl:stylesheet> 

Это прекрасно работает и дает мне желаемых результатов.

Type Count 
A  2 
B  1 
C  1 

Однако, если бы я должен был добавить другой элемент Records, он, кажется, обрабатывал два один за другим, например.

<?xml-stylesheet type="text/xsl" href="merge2.xsl"?> 

<Main> 
    <Records> 
     <Record> 
      <Description>A</Description> 
     </Record> 
     <Record> 
      <Description>A</Description> 
     </Record> 
     <Record> 
      <Description>B</Description> 
     </Record> 
     <Record> 
      <Description>C</Description> 
     </Record> 
    </Records> 
    <Records> 
     <Record> 
      <Description>B</Description> 
     </Record> 
     <Record> 
      <Description>A</Description> 
     </Record> 
     <Record> 
      <Description>C</Description> 
     </Record> 
     <Record> 
      <Description>C</Description> 
     </Record> 
    </Records> 
</Main> 

Это приведет к следующим.

Type  Count 
A   3 
B   2 
C   3 
B   2 
A   3 
C   3 

Где он, кажется, обрабатывает первый экземпляр записей, а затем переходите к следующему. Есть ли способ сделать это так, чтобы он удалял дубликаты между ними?

Я попытался изменить каждый из них, чтобы пройти через каждый экземпляр записей, и попытался создать для него отдельный шаблон, однако мне все еще кажется, что что-то не хватает, поскольку мне не удалось заставить его работать.

Большое спасибо за помощь.

ответ

4

Попробуйте заменить preceding-sibling на preceding на ваш xsl:if statement.

Вы получили правильную идею для проведения вашего теста, чтобы вы излучали только tr один раз за столкновение Description. Однако preceding-sibling перестает проверять обратные дочерние элементы родителя; preceding продолжает проверку ранее в документе, и это то, что вы хотите избежать дублирования в Records.

Fyi, есть также опечатка, где Record( должен быть Record[. Вот полные, оперативные трансформации, включая те изменения:

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" 
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="html"/> 
    <xsl:template match="/"> 
    <table border="1"> 
     <tr> 
     <th>Type</th> 
     <th>Count</th> 
     </tr> 
     <xsl:for-each select="Main/Records/Record"> 
     <xsl:if test ="not(preceding::Record[Description/text() = current()/Description/text()])"> 
      <tr> 
      <td><xsl:value-of select="Description"/></td> 
      <td><xsl:value-of select="count(//Record[Description/text()=current()/Description/text()])"/></td> 
      </tr> 
     </xsl:if> 
     </xsl:for-each> 
    </table> 
    </xsl:template> 
</xsl:stylesheet> 
+0

Привет спасибо за ответ. Вы совершенно правы, не можете поверить, что я пропустил это. Большое спасибо. – user2795778

1

Несмотря на то, @kjhughes смог ответить на вопрос, у вас есть, для более эффективного подхода вы хотите использовать Muenchain Method.

Для этого вы должны определить ключ к группе на

<xsl:key name="Record-by-Description" match="Record" use="Description"/> 

Затем группа те элементы, использующие этот ключ

<xsl:apply-templates select="Record[generate-id() = generate-id(key('Record-by-Description', Description)[1])]" mode="group"/> 

, а затем использовать этот ключ, чтобы рассчитывать только те конкретные пункты

<xsl:value-of select="count(key('Record-by-Description', Description))"/> 

Этот процесс намного эффективнее и не требует навигации по всей структуре каждый раз.

Таким образом, в целом, когда вы берете этот XML

<Main> 
    <Records> 
    <Record> 
     <Description>A</Description> 
    </Record> 
    <Record> 
     <Description>A</Description> 
    </Record> 
    <Record> 
     <Description>B</Description> 
    </Record> 
    <Record> 
     <Description>C</Description> 
    </Record> 
    </Records> 
    <Records> 
    <Record> 
     <Description>B</Description> 
    </Record> 
    <Record> 
     <Description>A</Description> 
    </Record> 
    <Record> 
     <Description>C</Description> 
    </Record> 
    <Record> 
     <Description>C</Description> 
    </Record> 
    </Records> 
</Main> 

И применить этот XSLT к нему

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
    <xsl:output method="xml" indent="yes" /> 
    <xsl:key name="Record-by-Description" match="Record" use="Description"/> 

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

    <xsl:template match="Main"> 
    <table> 
     <tr> 
     <th>Type</th> 
     <th>Count</th> 
     </tr> 
     <xsl:apply-templates select="Records"/> 
    </table> 
    </xsl:template> 

    <xsl:template match="Records"> 
    <xsl:apply-templates select="Record[generate-id() = generate-id(key('Record-by-Description', Description)[1])]" mode="group"/> 
    </xsl:template> 

    <xsl:template match="Record" mode="group"> 
    <tr> 
     <td> 
     <xsl:value-of select="Description"/> 
     </td> 
     <td> 
     <xsl:value-of select="count(key('Record-by-Description', Description))"/> 
     </td> 
    </tr> 
    </xsl:template> 

</xsl:stylesheet> 

Он производит этот выход

<table> 
    <tr> 
    <th>Type</th> 
    <th>Count</th> 
    </tr> 
    <tr> 
    <td>A</td> 
    <td>3</td> 
    </tr> 
    <tr> 
    <td>B</td> 
    <td>2</td> 
    </tr> 
    <tr> 
    <td>C</td> 
    <td>3</td> 
    </tr> 
</table> 
+0

Привет, спасибо за ответ. Великое объяснение, метод Muenchain - это то, что я видел, когда люди говорили о поиске ответа, но так и не поняли его полностью, поэтому спасибо вам за помощь. – user2795778

+0

Я попробовал ваш пример, и он отлично работает. Можно ли использовать этот метод для нескольких xml-файлов, если бы у меня был index.xml, содержащий список файлов? Большое спасибо. – user2795778

+0

@ user2795778 Я считаю, что до тех пор, пока у вас есть ключ, который будет работать. Я думаю, что может потребоваться объединить другие файлы в один первый, но затем создать ключ в фрагменте результирующего дерева всего XML, собранного вместе. Я мог бы быть действительно хорошим вопросом, чтобы спросить, есть ли у вас пример. –

Смежные вопросы