XSL Получить все инструкции по обработке между текущим элементом и предыдущим элементом

Учитывая следующий образец xml:

<?xml version = "1.0" encoding = "UTF-8"?>
<root>
    <data>
        <?format-number id?>
        <id>123</id>
        <?xml-multiple comment?>
        <?format-string comment?>
        <comment>hello</comment>
        <?format-string comment?>
        <comment>goodbye</comment>
        <text>bar</text>
    </data>
</root>

Мне нужно написать XSL, который копирует заданный элемент, а также все предыдущие инструкции по обработке до предыдущего элемента (родственного или родственного). Копия инструкций по обработке должна быть общей (неважно, что это за инструкции, если они логически «принадлежат» к элементу (т.е. воспринимайте их как аннотации к последующему элементу, и их может быть несколько) Также не имеет значения, какой у предыдущего элемента (опять же, это может быть то же имя элемента, другое имя элемента или родительский элемент).

XSL может выглядеть примерно так:

<?xml version = "1.0" encoding = "UTF-8" standalone = "no"?>
<xsl:stylesheet xmlns:fn = "http://www.w3.org/2005/xpath-functions"
  xmlns:xs = "http://www.w3.org/2001/XMLSchema" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
  version = "2.0">
  <xsl:output byte-order-mark = "no" encoding = "UTF-8" indent = "yes" method = "xml"
    omit-xml-declaration = "yes" />


  <xsl:template match = "/">
    <root>
      <xsl:for-each select = "/root/data">
        <data>
          <xsl:for-each select = "comment">
            <!-- some select usage of preceding-sibling and processing-instruction() ? -->
            <xsl:copy-of select = "???" />
            <xsl:copy-of select = "." />
            <foo>bar</foo>
          </xsl:for-each>
          <xsl:for-each select = "text">
            <xsl:copy-of select = "." />
          </xsl:for-each>
        </data>
      </xsl:for-each>
    </root>
  </xsl:template>
</xsl:stylesheet>

Исходя из вышеизложенного, результат должен быть:

<?xml version = "1.0" encoding = "UTF-8"?>
<root>
    <data>
        <?xml-multiple comment?>
        <?format-string comment?>
        <comment>hello</comment>
        <?format-string comment?>
        <comment>goodbye</comment>
        <text>bar</text>
    </data>
</root>

Обновлять

Дополнительный контекст. У меня есть несколько существующих xsl, которые были написаны/протестированы таким образом, чтобы нацеливаться на определенные элементы по мере их преобразования (т. е. интенсивное использование for-each select = "element"). Этот xsl был сгенерирован из MapForce (графический картографический инструмент), но не копирует инструкции по обработке xml!. В идеале я мог бы расширить этот существующий xsl таким образом, чтобы он был относительно ненавязчивым (например, добавляя какой-нибудь xsl прямо перед каждым copy-of внутри for-each, который копирует инструкции по обработке этих конкретных элементов.

По сути, трюк состоит в том, чтобы включить фрагмент, который говорит: «внутри for-each, который выбирает элемент, сначала скопируйте все инструкции по обработке текущего элемента (те, которые идут до текущего элемента, но после предыдущего элемента), а затем выполните любую логику. уже есть, т.е. больше copy-of и т. д.

Обновление 2

После дальнейшего тестирования приведенного ниже ответа на основе key появился еще один пограничный случай, в котором иногда встречаются «осиротевшие» инструкции по обработке, то есть те, которые не аннотируют элемент following-sibling, но идут в конце, т.е. <?xml-multiple colors?> ниже.

<?xml version = "1.0" encoding = "UTF-8"?>
<root>
    <data>
        <?format-number id?>
        <id>123</id>
        <?xml-multiple comment?>
        <?format-string comment?>
        <comment>hello</comment>
        <?format-string comment?>
        <comment>goodbye</comment>
        <?format-string text?>
        <text>bar</text>
        <?xml-multiple colors?>
    </data>
</root>

В этом случае результат должен быть

<?xml version = "1.0" encoding = "UTF-8"?>
<root>
    <data>
        <?xml-multiple comment?>
        <?format-string comment?>
        <comment>hello</comment>
        <?format-string comment?>
        <comment>goodbye</comment>
        <text>bar</text>
        <?xml-multiple colors?>
    </data>
</root>

Это сложно, потому что у него нет following-sibling, скорее, у него нет «целевого» элемента. В связи с этим я также понял, что инструкции по обработке «данных» (т. Е. Если format-number — это имя, id — это данные) — это действительно то, что связывает их с целью. В этом отношении я действительно хочу сделать копию инструкций по обработке на основе элемента, на который они нацелены. Другими словами, я думаю, что хочу создать ключ на основе узла, указанного в инструкции обработки, и следующего брата.

<xsl:copy-of select = "key('pi', generate-id('foo'))" />

вычеркнет инструкции по обработке, которые были проиндексированы, потому что они были найдены как:

<?format-number foo?>
<id>123</id>

Если вы используете XSLT 2.0, прикрепите этот тег к своему вопросу; гораздо легче ответить на этот вопрос, если вы хорошо знаете версию XSLT, которую используете.

Michael Kay 09.02.2023 08:47
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
59
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не уверен, что полностью следую вашему описанию. Возможно, что-то вроде этого может сработать для вас:

XSLT 2.0

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

<xsl:template match = "/root">
    <root>
        <xsl:for-each select = "data">
            <data>
                <xsl:for-each-group select = "* | processing-instruction()" group-ending-with = "*">
                    <xsl:if test = "current-group()[last()][self::comment]">
                        <xsl:copy-of select = "current-group()" />
                    </xsl:if>
                </xsl:for-each-group>
                <xsl:copy-of select = "text" />
            </data>
        </xsl:for-each>
    </root>
</xsl:template>

</xsl:stylesheet>

Кажется, это работает! Итак, если бы я хотел сделать это несколько в общем, похоже, я мог бы использовать один и тот же стиль for-each-group для каждого узла, который я выбираю. Другими словами, вот так (замена copy-of для элемента text на аналогичный for-each-group, но с другим именем self::tag-name). Кстати говоря, не то чтобы это критично, но могу ли я переместить условие if для имени узла в for-each-group select?

sparty02 08.02.2023 20:28

Кроме того, есть ли версия, в которой for-each-group копирует инструкции по обработке, которые предшествуют элементу, но не сам элемент?

sparty02 08.02.2023 20:37

Вы можете изменить последнее условие [self::comment], чтобы включить больше элементов. Чтобы исключить копирование самого элемента, вы можете сделать <xsl:copy-of select = "current-group()[not(self::*)]"/>.

michael.hor257k 08.02.2023 20:52

Проблема, с которой я сталкиваюсь (см. Обновление в моем вопросе), заключается в том, что это связано с обновлением существующего xsl, и я хотел бы попытаться избежать его слишком большой перестройки.

sparty02 08.02.2023 21:02

Боюсь, я не вижу ваш существующий XSLT, поэтому я не знаю, что означает «слишком много переделывать». Вы можете использовать xsl:apply-templates вместо xsl:copy-of и добавить шаблон для каждого элемента. Или, возможно, используйте другой метод, который я только что опубликовал.

michael.hor257k 08.02.2023 21:50
Ответ принят как подходящий

Вот другой метод для достижения того же результата, который вы можете найти более удобным для адаптации к вашей ситуации:

XSLT 1.0

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

<xsl:key name = "pi" match = "processing-instruction()" use = "generate-id(following-sibling::*[1])" />

<xsl:template match = "/root">
    <root>
        <xsl:for-each select = "data">
            <data>
                <xsl:for-each select = "comment">
                    <xsl:copy-of select = "key('pi', generate-id())" />
                    <xsl:copy-of select = "." />
                </xsl:for-each>
            </data>
        </xsl:for-each>
    </root>
</xsl:template>

</xsl:stylesheet>

да! это выглядит очень чистым и, судя по ранним испытаниям дыма, выглядит великолепно. прежде чем копаться в том, как работают ключи xsl, я предполагаю, что это что-то вроде «назначить ключ каждой инструкции обработки (из-за атрибута match) со значением вызова функции generate-id на following-sibling::*[1]. Если это так, это кажется как говорится, возьмите первый экземпляр, т. е. [1], всех родственных узлов.Если это так, то как они связываются с элементом в области видимости, т. е. что повторяется для каждого?

sparty02 08.02.2023 22:20

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

michael.hor257k 08.02.2023 22:34

но если бы было несколько инструкций обработки, инструкция обработки получила бы идентификатор инструкции обработки 2 (т.е. это первый следующий за ним элемент)? Разве не нужно получать идентификатор первого элемента, не относящегося к инструкции по обработке, следующего за родственным элементом?

sparty02 08.02.2023 22:41

Инструкция обработки не является элементом (и наоборот): w3.org/TR/xpath-datamodel/#Node

michael.hor257k 08.02.2023 22:47

еще одна вещь, которую я заметил, это то, что могут быть моменты, когда мне нужно передать ссылку на generate-id. В моем случае я знаю, на какой элемент нацелен, поэтому, если нужно, я могу просто сказать generate-id(<expression-targetting-element>), что отлично работает.

sparty02 08.02.2023 22:49

см. мое последнее обновление. Я думаю, мне нужно настроить «индекс», который использует мой ключ, чтобы он основывался на целевом узле инструкций по обработке (раздел данных инструкции по обработке). должно быть прямо?

sparty02 08.02.2023 23:49

в соответствии с этими строками инструкция обработки, которая была проиндексирована ключом, будет находиться в контексте своего родительского элемента с возможностью также нацеливаться на (несуществующий) элемент. т. е. data/<?format-string comment?> будет нацелен на данные/комментарий (и будет доступен для инъекций в то время как for-each над данными/комментариями. однако data/<?xml-multiple color?> будет нацелен на данные/цвет, но данные/ цвет может не обязательно быть там. Однако я все равно хотел бы ввести эту инструкцию обработки в результат, чтобы отразить «пустой список» (в конечном итоге используемый ниже по течению для преобразования формата данных)

sparty02 09.02.2023 00:00

Вы можете выбрать «оставшиеся» PI, используя key('pi', ''). --- Тот факт, что PI содержат имя элемента, которому они "принадлежат", кажется неуместным, поскольку это имя не уникально. Если только между PI и его «истинным» элементом не может быть другого элемента с другим именем.

michael.hor257k 09.02.2023 00:55

Если подумать, вы могли бы использовать этот факт для получения основной части вашего результата, используя просто: <xsl:copy-of select = "processing-instruction()[.='comment'] | comment" />. Но тогда выбор «осиротевших» ИП будет не таким тривиальным — возможно, что-то вроде: <xsl:copy-of select = "processing-instruction()[not(.=../*/name())]"/>.

michael.hor257k 09.02.2023 01:47

что делает key('pi', '')? Мне интересно, как он знает, что нужно получать инструкции по обработке только внутри контекста для каждого по сравнению со всеми из них.

sparty02 09.02.2023 16:18

Я попробовал подход processing-instruction()[.='comment'], но мне нужно быть внутри for-each (исходя из того, как изначально был сгенерирован xsl), так что на самом деле это не работает так же. Если я помещу его за пределами for-each, он не совсем будет ассоциироваться с каждым узлом в for-each (посмотрите, как в первом комментарии есть два pi, а во втором ни одного... потому что я сделал это до for- см. здесь xsltransform.net/gVhDDzC

sparty02 09.02.2023 16:20

другими словами, xml <xsl:for-each select = "comment"> <!-- what would I do here?--> <xsl:copy-of select = "processing-instruction()[.='comment']" /> <xsl:copy-of select = "." /> </xsl:for-each>

sparty02 09.02.2023 16:21

единственное, что касается ключевого подхода, это то, что я действительно не понимаю, как работает ключ ('pi', ''). В противном случае, для контекста, то, что я изначально пытался сделать, было смесью «внутри for-each, ищите preceding-sibling назад, пока не нажмете следующий узел, а затем захватите их, пока не нажмете текущий узел». Итак, «дайте мне все инструкции по обработке между текущим узлом и предыдущим узлом»

sparty02 09.02.2023 16:24

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

michael.hor257k 09.02.2023 16:27

Давайте продолжим обсуждение в чате.

sparty02 09.02.2023 17:49

резюме в stackoverflow.com/questions/75401863/…

sparty02 09.02.2023 18:11

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