Отдельные элементы и группировки - PullRequest
8 голосов
/ 30 декабря 2008

Учитывая следующий фрагмент XML:

<Problems>
  <Problem>
    <File>file1</File>
    <Description>desc1</Description>
  </Problem>
  <Problem>
    <File>file1</File>
    <Description>desc2</Description>
  </Problem>
  <Problem>
    <File>file2</File>
    <Description>desc1</Description>
  </Problem>
</Problems>

Мне нужно произвести что-то вроде

<html>
  <body>
    <h1>file1</h1>
    <p>des1</p>
    <p>desc2</p>
    <h1>file2</h1>
    <p>des1</p>
  </body>
</html>

Я пытался использовать ключ, как

<xsl:key name="files" match="Problem" use="File"/>

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

Ответы [ 3 ]

7 голосов
/ 30 декабря 2008

Это решение немного проще, более эффективно и в то же время более общее , чем представленное Ричардом:

Это преобразование :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--                                            -->
 <xsl:key name="kFileByVal" match="File"
       use="." />
<!--                                            -->
 <xsl:key name="kDescByFile" match="Description"
       use="../File"/>
<!--                                            -->
    <xsl:template match="/*">
     <html>
      <body>
      <xsl:for-each select=
         "*/File[generate-id()
                =
                 generate-id(key('kFileByVal',.)[1])]">
        <h1><xsl:value-of select="."/></h1>
        <xsl:for-each select="key('kDescByFile', .)">
          <p><xsl:value-of select="."/></p>
        </xsl:for-each>
      </xsl:for-each>
      </body>
     </html>
    </xsl:template>
</xsl:stylesheet>

применительно к предоставленному документу XML :

<Problems>
    <Problem>
        <File>file1</File>
        <Description>desc1</Description>
    </Problem>
    <Problem>
        <File>file1</File>
        <Description>desc2</Description>
    </Problem>
    <Problem>
        <File>file2</File>
        <Description>desc1</Description>
    </Problem>
</Problems>

Дает желаемый результат :

<html>
   <body>
      <h1>file1</h1>
      <p>desc1</p>
      <p>desc2</p>
      <h1>file2</h1>
      <p>desc1</p>
   </body>
</html>

Запомните простой шаблон совпадения первого <xsl:key> и как, используя второй <xsl:key>, мы найдем все "Description "элементы, которые являются родственными элементами элемента" File ", имеющего заданное значение.

Мы могли бы использовать больше шаблонов вместо обработки извлечения <xsl:for-each>, однако это довольно простой случай, и решение действительно выигрывает от более коротких, более компактных и более читаемых код.

Также обратите внимание, что в XSLT 2.0 обычно используется <xsl:for-each-group> инструкция вместо метода Мюнхена .

5 голосов
/ 30 декабря 2008

Вот как я это сделаю, используя метод Мюнхена. Google 'xslt muenchean' для получения дополнительной информации от умных людей. Может быть, умный путь, но я оставлю это другим.

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

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html"/>
    <xsl:key name="files" match="/Problems/Problem/File" use="./text()"/>
    <xsl:template match="/">
        <html>
            <body>
                <xsl:apply-templates select="Problems"/>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="Problems">
        <xsl:for-each select="Problem/File[generate-id(.) = generate-id(key('files', .))]">
            <xsl:sort select="."/>
            <h1>
                <xsl:value-of select="."/>
            </h1>
            <xsl:apply-templates select="../../Problem[File=current()/text()]"/>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="Problem">
        <p>
            <xsl:value-of select="Description/text()"/>
        </p>
    </xsl:template>
</xsl:stylesheet>

Идея состоит в том, чтобы вводить каждый элемент File, используя его текстовое значение. Затем отображать только значения файла, если они совпадают с ключевым элементом. Чтобы проверить, одинаковы ли они, используйте generate-id. Существует аналогичный подход, когда вы сравниваете первый соответствующий элемент. Я не могу сказать вам, что более эффективно.

Я тестировал здесь код с помощью Marrowsoft Xselerator, моего любимого инструмента xslt, хотя он больше не доступен, на самом деле. Результат, который я получил:

<html>
<body>
<h1>file1</h1>
<p>desc1</p>
<p>desc2</p>
<h1>file2</h1>
<p>desc1</p>
</body>
</html>

Используется msxml4.

Я отсортировал вывод по файлу. Я не уверен, что ты этого хотел.

Надеюсь, это поможет.

0 голосов
/ 19 сентября 2013

Это решение XSLT 1.0 также поможет. Чуть более лаконично, чем другие решения!

  <xsl:template match="/">           
    <html><body>
      <xsl:for-each select="//File[not(.=preceding::*)]">
        <h1><xsl:value-of select="." /></h1>
        <xsl:for-each select="//Problem[File=current()]/Description">
          <p><xsl:value-of select="." /></p>
        </xsl:for-each>
      </xsl:for-each>
    </body></html>
  </xsl:template>

Результат:

<html xmlns="http://www.w3.org/1999/xhtml">
  <body>
    <h1>file1</h1>
    <p>desc1</p>
    <p>desc2</p>
    <h1>file2</h1>
    <p>desc1</p>
  </body>
</html>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...