Отчетливые элементы и группировка

Учитывая следующий фрагмент 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"/>

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
0
10 115
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Вот как я бы это сделал, используя метод Мюнхея. 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, хотя он больше не доступен, afaik. В результате я получил:

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

Это использует msxml4.

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

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

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

Richard A 30.12.2008 05:03

@rjonston: Я предложил немного более чистое и, вероятно, более эффективное решение, чем Richards.

Dimitre Novatchev 30.12.2008 09:49

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

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

<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> pull-processing, однако это довольно простой случай, и решение действительно выигрывает от более короткого, более компактного и более читаемого кода.

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

@Dimitre. Я когда-либо использовал ключи только для группировки Муэнчей. Спасибо за пример более широкого использования. Кроме того, я еще не рассматривал XSLT 2.0, поэтому для каждой группы тоже интересно. Ура и с Новым годом.

Richard A 31.12.2008 02:55

@ Ричард: С Новым годом тебя, Ричард.

Dimitre Novatchev 31.12.2008 06:16

Это решение 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>

Другие вопросы по теме