XSLT Найти значение элемента, которого нет в родственных элементах

У меня есть список элементов A:

    <a sign = "0">100<a/>
    <a sign = "1">150<a/>
    <a sign = "2">200<a/>
    <a sign = "0">250<a/>
    <a sign = "3">100<a/>

и еще один список элементов B:

    <b>
      <sign>1</sign>
      <sign>3</sign>
    </b>
    <b></b> <!--   no sign    -->
    

Во-первых, для каждого <b> я хочу суммировать все значения <a>, имеющие тот же знак, что и b. Я делаю это с помощью рекурсии:

<xsl:template name = "sumCount">
    <xsl:param name = "nodes" /> <!--   the a elements    -->
    <xsl:param name = "signs"/>  <!--   the signs of the b element    -->
    <xsl:param name = "tempSum" select = "0" />
    <xsl:choose>
        <xsl:when test = "$nodes and $signs">
            <xsl:variable name = "countSum" select = "sum($nodes[@sign=$signs[1]])" />
            <xsl:call-template name = "sumItems">
                <xsl:with-param name = "nodes" select = "$nodes" />
                <xsl:with-param name = "tempSum" select = "number($tempSum) + number($countSum)" />
                <xsl:with-param name = "signs" select = "$signs[position() > 1]"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select = "$tempSum" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

Но теперь, если <b> не имеет знаков, я хочу найти сумму всех элементов из A, у которых нет знака, который является знаком одного из дочерних элементов моего фактического узла <b>. (Проще говоря, я хочу найти именно все остальные узлы, которые не будут обнаружены при первом суммировании).

Итак, в этом примере для второго <b> без знака я хотел бы найти <a> со знаком 0 и 2 (и все остальные числа, кроме 1 и 3). Я мог бы выбрать все остальные узлы <b>, но тогда мне понадобится что-то вроде:

select = "sum($nodes[@sign *not in* $otherBNodes)"

Есть ли что-то подобное в xslt 1.0? Как я могу этого добиться?


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

Проблема в том, что мои узлы <a> не записаны в корневом XML-файле, а генерируются в xsl, а затем становятся доступными с помощью ext:node-set. Я отредактировал ваш ответ, чтобы продемонстрировать это.

XML:

<root>
<b id = "1">
    <sign>1</sign>
    <sign>3</sign>
</b>
<b id = "2"/>
</root>

XSLT 1.0

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

<xsl:template match = "/root">
    <xsl:variable name = "cNodes">
        <xsl:element name = "a">
            <xsl:attribute name = "sign"><xsl:value-of select = "1"/></xsl:attribute>
            <xsl:value-of select = "100"/>
        </xsl:element>
        <xsl:element name = "a">
            <xsl:attribute name = "sign">
                <xsl:value-of select = "1"/>
                <xsl:value-of select = "2"/>
            </xsl:attribute>
            <xsl:value-of select = "150"/>
        </xsl:element>
        <xsl:element name = "a">
            <xsl:attribute name = "sign"><xsl:value-of select = "3"/></xsl:attribute>
            <xsl:value-of select = "200"/>
        </xsl:element>
        <xsl:element name = "a">
            <xsl:attribute name = "sign"><xsl:value-of select = "4"/></xsl:attribute>
            <xsl:value-of select = "250"/>
        </xsl:element>
    </xsl:variable>

    <xsl:for-each select = "ext:node-set(a)"> <!-- switch context-->
        <xsl:key name = "a" match = "a" use = "@sign" />
        <xsl:variable name = "all_a" select = "a" />
        <xsl:copy>
                <b id = "1">
                    <xsl:value-of select = "sum(key('a', 1))"/> <!-- I did not think about how to bring b here yet, so I take 1 for now-->
                </b>
        </xsl:copy>
    </xsl:for-each>

Конечно, это не работает, хотя я могу перебирать узлы <a>, я почему-то не могу создать ключ.

Сколько b элементов может быть?

michael.hor257k 09.10.2023 19:25

Этот xpath будет суммировать 0 и 2 элемента независимо от пустого b sum(//a[not(@sign = //b/sign)])

LMC 09.10.2023 19:29
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
62
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Я бы предложил радикально иной подход:

XML

<root>
    <a sign = "0">100</a>
    <a sign = "1">150</a>
    <a sign = "2">200</a>
    <a sign = "0">250</a>
    <a sign = "3">100</a>
    <b id = "1">
        <sign>1</sign>
        <sign>3</sign>
    </b>
    <b id = "2"/>
</root>

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 = "a" match = "a" use = "@sign" />

<xsl:template match = "/root">
    <xsl:variable name = "all_a" select = "a" />
    <xsl:variable name = "matched_a" select = "key('a', b/sign)" />
    <xsl:copy>
        <xsl:for-each select = "b">
            <b id = "{@id}">
                <xsl:choose>
                    <xsl:when test = "sign">
                        <xsl:value-of select = "sum(key('a', sign))"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select = "sum($all_a) - sum($matched_a)"/>
                    </xsl:otherwise>
                </xsl:choose>
            </b>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Результат

<?xml version = "1.0" encoding = "UTF-8"?>
<root>
   <b id = "1">250</b>
   <b id = "2">550</b>
</root>

Добавлен:

Если вам действительно нужно создать элементы a, жестко закодировав их внутри таблицы стилей, рассмотрите что-то вроде:

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

<xsl:key name = "a" match = "a" use = "@sign" />

<xsl:template match = "/root">
    <xsl:variable name = "a-rtf">
        <a sign = "0">100</a>
        <a sign = "1">150</a>
        <a sign = "2">200</a>
        <a sign = "0">250</a>
        <a sign = "3">100</a>
    </xsl:variable>
    <xsl:variable name = "a-set" select = "exsl:node-set($a-rtf)" />
    <xsl:variable name = "all_signs" select = "b/sign" />
    <!-- output -->
    <xsl:copy>
        <xsl:for-each select = "b">
            <b id = "{@id}">
                <xsl:variable name = "sign" select = "sign" />
                <!-- switch context to the variable document -->
                <xsl:for-each select = "$a-set">
                    <xsl:choose>
                        <xsl:when test = "$sign">
                            <xsl:value-of select = "sum(key('a', $sign))"/>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:value-of select = "sum($a-set/a) - sum(key('a', $all_signs))"/>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each>
            </b>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Большое спасибо за ответ, я еще не знал о такой возможности :) Но есть одна проблема, мой пример немного упрощен. Я получаю свои узлы <a> как набор узлов, и, как я узнал из какого-то другого вашего сообщения, я не могу использовать поиск по ключу для <a>, когда я нахожусь в <for-each> из <b>. Есть ли у вас решение этой проблемы?

Kaffetrinken 10.10.2023 12:47

Я не уверен, что вы подразумеваете под «Я получаю свои узлы <a> как набор узлов».

michael.hor257k 10.10.2023 13:02

Элементы <a> созданы мной и поэтому передаются шаблону через ext:node-set($all_a). Итак, если я правильно понял, мне нужно попасть в этот новый контекст документа, я пытаюсь добиться этого, выполнив <xsl:for-each> в моей переменной $all_a. Но мой ключ <xsl:key name="a_by_sign" match="a" use="@sign"/> ничего не выбирает. Может быть, потому что мой for-each уже перебирает <a>? Я попробовал <xsl:key name="a_by_sign" match="../a" use="@sign"/>, но безуспешно

Kaffetrinken 10.10.2023 15:07

«Элементы <a> созданы мной». Пожалуйста, отредактируйте свой вопрос и покажите эту часть в своей таблице стилей.

michael.hor257k 10.10.2023 15:25

Я добавил дополнительный контекст

Kaffetrinken 10.10.2023 17:05

Извините, это по-прежнему невоспроизводимая проблема. Вы показываете, что для каждого [чего-то, чего мы не видим], создается элемент a. Где это нечто и почему нельзя сослаться на него напрямую?

michael.hor257k 10.10.2023 18:48

Я думал не обязательно, не всегда результатом будет просто список элементов <a>, который записывается в переменную a_nodes? И если вы сделаете эти элементы доступными через ext:node-set($a_nodes), имеет ли значение, как и где они были созданы? В моем понимании это будет отдельный документ. Пожалуйста, поправьте меня, если я здесь ошибаюсь. Фраза for-each, которую я вырезал, перебирает совершенно другой набор элементов, считывает некоторые значения и записывает их в элемент <a>.

Kaffetrinken 10.10.2023 19:05

Дело в том, что создаваемая вами переменная действительно является отдельным документом, что усложняет ситуацию, поскольку в XSLT 1.0 ключи работают только в текущем документе. Поэтому, если вы настаиваете на этом пути, вам нужно будет сохранить sign элементы текущего b в переменной, переключить контекст на другой документ и вызвать ключ, используя переменную (см. здесь только один пример: stackoverflow .com/a/32440143/3016153). OTOH, если вы укажете ключ на исходные узлы, вы сможете избежать всего этого джаза. Похоже, между ними существует соотношение 1 к 1, поэтому я не понимаю, почему бы вам этого не сделать.

michael.hor257k 10.10.2023 19:29

Боюсь, мне придется придерживаться этих наборов узлов... Но я все равно как-то не понимаю. Я пытался переключить контекст с помощью <for-each select=ext:node-set($cNodes)>, и хотя я могу перебирать свои a_nodes, мой ключ <xsl:key name="a" match="a" use= "@sign"/> не работает. Я еще раз отредактировал вторую часть своего вопроса, был бы очень благодарен, если бы вы посмотрели еще раз.

Kaffetrinken 10.10.2023 21:04

Посмотрите, имеет ли добавленная версия смысл. Я все еще подозреваю, что это слишком усложняет ситуацию.

michael.hor257k 10.10.2023 21:33

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

Kaffetrinken 11.10.2023 08:51

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

michael.hor257k 11.10.2023 09:07

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

Kaffetrinken 11.10.2023 09:14

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