#java #xml #xslt
#java #xml #xslt
Вопрос:
Я долгое время работал над проблемой. Мне нужно удалить дубликаты из XML-файла на основе значения ключа дочернего тега. Родительский тег «A» всегда будет известен и останется неизменным. Вложенные теги могут иметь разные имена, т. Е. могут быть «Name», «Location», «Name». Если данные под 2 тегами «Name» являются дубликатами друг друга, один из тегов name вместе с его дочерними узлами должен быть удален. Это должно произойти только в том случае, если все значения ключа дочернего тега одинаковы, а не если только один или 2 или более тегов одинаковы, но существуют некоторые теги с другим значением ключа или с тем же ключом и другим значением под родительским тегом.
Пример:
`<A>
<Name>
<c>1<c>
<d>g</d>
<e>h</e>
</Name>
<Location>
<c>2<c>
<d>g</d>
<e>h</e>
</Location>
<Name>
<c>1<c>
<d>g</d>
<e>h</e>
</Name>
<A>`
Ожидаемый результат:
`<A>
<Name>
<c>1<c>
<d>g</d>
<e>h</e>
</Name>
<Locaiton>
<c>2<c>
<d>g</d>
<e>h</e>
</Locaiton>
<A>`
Я пробовал: это:
`<xsl:template match="@*|node()">
<xsl:if test="not(node()) or not(preceding-sibling::node()[.=string(current())])">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template> `
но в итоге произошло то, что дочерние теги с одинаковыми значениями ключа также были удалены, и я получал что-то вроде этого:
`<A>
<Name>
<c>1<c>
<d>g</d>
<e>h</e>
</Name>
<Location>
<c>2<c>
</Location>
<A>`
Я ищу общий способ, поскольку я не хочу указывать значения тегов или ключи в файле.
Заранее спасибо :)!
Комментарии:
1. Какая версия XSLT?
2. Приведенный выше использует 1.0
Ответ №1:
В XSLT 3 может быть достаточно использования составного ключа с for-each-group group-by
:
<xsl:template match="A">
<xsl:copy>
<xsl:for-each-group select="*" composite="yes" group-by="*">
<xsl:apply-templates select="."/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
Если внуки также не подлежат сортировке, вам может понадобиться
<xsl:for-each-group select="*" composite="yes" group-by="sort(*, (), function($c) { name($c) })">
вместо простого группирования, указанного выше.
В обоих случаях, в качестве базового преобразования, вам необходимо настроить преобразование идентификатора, объявив <xsl:mode on-no-match="shallow-copy"/>
дочерним элементом xsl:stylesheet
(или xsl:transform
) в XSLT.
Но проблема скорее недоопределена, неясно, всегда ли имена и порядок дочерних элементов просто неизвестны или могут быть вариации и как с ними обращаться.
В качестве альтернативы, если у вас могут быть разные элементы в качестве дочерних элементов A
, но вам нужно устранить дубликаты только для определенного, подобного B
, но для возможных других элементов, тогда может помочь ключ, явно объявленный для B
элементов
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:key name="B-group" match="A/B" use="sort(*, (), function($c) { name($c) })"/>
<xsl:template match="B[not(. is key('B-group', sort(*, (), function($c) { name($c) }))[1])]"/>
</xsl:stylesheet>
Комментарии:
1. Порядок дочерних элементов и имен всегда будет неизвестен. Элементы могут отображаться в любом порядке и могут содержать любые данные. Необходимо убедиться, что в файле XML, если есть 2 родительских тега с точно такими же значениями дочернего ключа, один из всех родительских тегов должен быть удален вместе с его дочерними узлами.
2. Также, пожалуйста, обратите внимание, что имя тегов может быть любым, кроме верхнего XML-тега, так что «A» будет там, но теги «B» или «c» или «d» могут быть чем угодно, например, «b» может быть «именами» и так далее
3. Я новичок в XSTL и знаю об этом. Получил это после запуска выше XSTL 3: ОШИБКА: ‘Неподдерживаемый элемент XSL’ w3.org/1999/XSL/Transform:for-each-group » javax.xml.transform. Исключение TransformerException: java.lang.RuntimeException: неподдерживаемый элемент XSL ‘ w3.org/1999/XSL/Transform:for-each-group ‘ в
4. Ну, чтобы использовать XSLT 3, вам нужно использовать процессор XSLT 3, в мире Java это означает добавление Saxon 10 или 9.9 или 9.8 в путь к классу.
5. И это помогло бы, если бы вы отредактировали свой вопрос и каким-то образом детализировали тип ввода, который вы можете иметь, неясно, что означает «может содержать любые данные», например, если внуки, которых вы называете ключами, могут иметь дополнительное содержимое элемента или только обычный текст. Также неясно, будут ли все дочерние элементы
A
иметь одно и то же имя (даже если оно неизвестно) или могут быть дочерние элементы с разными именами, и любая дедупликация также должна учитывать имя элемента.