У меня есть список элементов 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>, я почему-то не могу создать ключ.
Этот xpath будет суммировать 0 и 2 элемента независимо от пустого b sum(//a[not(@sign = //b/sign)])





Я бы предложил радикально иной подход:
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>. Есть ли у вас решение этой проблемы?
Я не уверен, что вы подразумеваете под «Я получаю свои узлы <a> как набор узлов».
Элементы <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"/>, но безуспешно
«Элементы <a> созданы мной». Пожалуйста, отредактируйте свой вопрос и покажите эту часть в своей таблице стилей.
Я добавил дополнительный контекст
Извините, это по-прежнему невоспроизводимая проблема. Вы показываете, что для каждого [чего-то, чего мы не видим], создается элемент a. Где это нечто и почему нельзя сослаться на него напрямую?
Я думал не обязательно, не всегда результатом будет просто список элементов <a>, который записывается в переменную a_nodes? И если вы сделаете эти элементы доступными через ext:node-set($a_nodes), имеет ли значение, как и где они были созданы? В моем понимании это будет отдельный документ. Пожалуйста, поправьте меня, если я здесь ошибаюсь. Фраза for-each, которую я вырезал, перебирает совершенно другой набор элементов, считывает некоторые значения и записывает их в элемент <a>.
Дело в том, что создаваемая вами переменная действительно является отдельным документом, что усложняет ситуацию, поскольку в XSLT 1.0 ключи работают только в текущем документе. Поэтому, если вы настаиваете на этом пути, вам нужно будет сохранить sign элементы текущего b в переменной, переключить контекст на другой документ и вызвать ключ, используя переменную (см. здесь только один пример: stackoverflow .com/a/32440143/3016153). OTOH, если вы укажете ключ на исходные узлы, вы сможете избежать всего этого джаза. Похоже, между ними существует соотношение 1 к 1, поэтому я не понимаю, почему бы вам этого не сделать.
Боюсь, мне придется придерживаться этих наборов узлов... Но я все равно как-то не понимаю. Я пытался переключить контекст с помощью <for-each select=ext:node-set($cNodes)>, и хотя я могу перебирать свои a_nodes, мой ключ <xsl:key name="a" match="a" use= "@sign"/> не работает. Я еще раз отредактировал вторую часть своего вопроса, был бы очень благодарен, если бы вы посмотрели еще раз.
Посмотрите, имеет ли добавленная версия смысл. Я все еще подозреваю, что это слишком усложняет ситуацию.
Я перепробовал все, но ключевая функция в вашем примере всегда возвращает мне 0. Вчера я попробовал почти то же самое для своего документа с теми же результатами. Я согласен, что это, вероятно, слишком сложно, но эти элементы написаны в устаревшем коде, и я бы предпочел их не касаться...
Я проверил свой ответ с теми же входными данными, что и раньше (за исключением элементов a), и результат тот же, что и раньше. Боюсь, я не знаю, как помочь вам с проблемой, которую не могу воспроизвести.
О боже... похоже, трансформация IntelliJ здесь заканчивается. Я протестировал его в веб-решении, и он работает нормально. Спасибо, что прошли весь путь со мной, это действительно мне очень помогло!
Сколько
bэлементов может быть?