У меня есть XML с двумя родительскими узлами (Base, Sub). Мне нужно написать XSLT, чтобы получить значения для условия ниже.
Условие: нужно получить разные элементы в обоих родителях.
Входной XML:
<?xml version = "1.0" encoding = "UTF-8"?>
<Data>
<Base>
<Student_ID>1234</Student_ID>
<Student_ID>1267</Student_ID>
<Student_ID>1890</Student_ID>
<Student_ID>5678</Student_ID>
<Student_ID>6743</Student_ID>
<Student_ID>8743</Student_ID>
</Base>
<Sub>
<Student_ID>5678</Student_ID>
<Student_ID>6743</Student_ID>
<Student_ID>3226</Student_ID>
<Student_ID>8123</Student_ID>
</Sub>
</Data>
Ожидаемый результат:
<?xml version = "1.0" encoding = "UTF-8"?>
<Data>
<Student_ID>1234</Student_ID>
<Student_ID>1267</Student_ID>
<Student_ID>1890</Student_ID>
<Student_ID>8743</Student_ID>
<Student_ID>3226</Student_ID>
<Student_ID>8123</Student_ID>
</Data>
Я пытался использовать ключ. Но это дает соответствующие значения. @SiebeJongebloed
Может быть, что-то вроде этого?
<xsl:stylesheet version = "2.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method = "xml" indent = "yes"/>
<xsl:key name = "student" match = "Student_ID" use = "." />
<xsl:template match = "/Data">
<xsl:copy>
<xsl:copy-of select = "Base/Student_ID[not(key('student', ., ../../Sub))]"/>
<xsl:copy-of select = "Sub/Student_ID[not(key('student', ., ../../Base))]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Другой способ, которым вы могли бы посмотреть на это:
XSLT 2.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:template match = "/Data">
<xsl:copy>
<xsl:for-each-group select = "*/Student_ID" group-by = ".">
<xsl:if test = "count(current-group())=1">
<xsl:copy-of select = "."/>
</xsl:if>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Обратите внимание, что это предполагает отсутствие дубликатов в каждой ветке.
В XSLT 3 вы также можете рассмотреть xsl:merge
, что позволит вам обрабатывать дубликаты в каждой ветке; недостатком является то, что слияние требует сортировки входных последовательностей по ключу(ам) слияния, поэтому результат
<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:xs = "http://www.w3.org/2001/XMLSchema"
expand-text = "yes"
exclude-result-prefixes = "#all"
version = "3.0">
<xsl:output method = "xml" indent = "yes"/>
<xsl:template match = "Data">
<xsl:copy>
<xsl:merge>
<xsl:merge-source name = "base" select = "Base/Student_ID" sort-before-merge = "yes">
<xsl:merge-key select = "."/>
</xsl:merge-source>
<xsl:merge-source name = "sub" select = "Sub/Student_ID" sort-before-merge = "yes">
<xsl:merge-key select = "."/>
</xsl:merge-source>
<xsl:merge-action>
<xsl:if test = "count(current-merge-group('base')) = 0 and count(current-merge-group('sub')) = 1 or count(current-merge-group('base')) = 1 and count(current-merge-group('sub')) = 0">
<xsl:copy-of select = "current-merge-group()"/>
</xsl:if>
</xsl:merge-action>
</xsl:merge>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
имеет правильные элементы, но в отсортированном порядке:
<Data>
<Student_ID>1234</Student_ID>
<Student_ID>1267</Student_ID>
<Student_ID>1890</Student_ID>
<Student_ID>3226</Student_ID>
<Student_ID>8123</Student_ID>
<Student_ID>8743</Student_ID>
</Data>
Конечно, мы могли бы пересортировать исходный порядок ввода, используя
<xsl:template match = "Data">
<xsl:copy>
<xsl:variable name = "unique-ids" as = "element(Student_ID)*">
<xsl:merge>
<xsl:merge-source name = "base" select = "Base/Student_ID" sort-before-merge = "yes">
<xsl:merge-key select = "."/>
</xsl:merge-source>
<xsl:merge-source name = "sub" select = "Sub/Student_ID" sort-before-merge = "yes">
<xsl:merge-key select = "."/>
</xsl:merge-source>
<xsl:merge-action>
<xsl:if test = "count(current-merge-group('base')) = 0 and count(current-merge-group('sub')) = 1 or count(current-merge-group('base')) = 1 and count(current-merge-group('sub')) = 0">
<xsl:sequence select = "current-merge-group()"/>
</xsl:if>
</xsl:merge-action>
</xsl:merge>
</xsl:variable>
<xsl:copy-of select = "$unique-ids/."/>
</xsl:copy>
</xsl:template>
Хорошее решение. Нельзя ли сделать оператор if несколько проще, вот так? <xsl:if test = "count(current-merge-group('base')) + count(current-merge-group('sub')) = 1 ">
@SiebeJongebloed, это хорошее упрощение, да.
@MartinHonnen Спасибо за поддержку, все работает отлично. Я новичок в XSLT, и не могли бы вы объяснить мне, как работает этот код и как одинаковые значения в Base и Sub исключаются при слиянии.
Начните с чтения w3.org/TR/xslt-30/#merging. Что касается слияния, current-merge-group()
содержит любые объединенные элементы из всех источников слияния (например, base
и sub
), но внутри xsl:merge-action
мы можем проверить, какая группа слияния содержит сколько элементов, и таким образом можно вывести объединенную группу, только если либо в группе base
или sub
был один предмет.
Другой вариант:
<xsl:for-each select = "distinct-values(*/Student_ID)">
<Student_ID>{.}</Student_ID>
</xsl:for-each>
Нет, это то, что я сначала тоже, но <Student_ID>5678</Student_ID>
нет в желаемом результате.
Ах точно .......
Что вы пробовали сами?