У меня есть следующий XML-код, который необходимо преобразовать, как указано в разделе «Ожидаемый результат», приведенном ниже.
Требования к трансформации:
Пример входного XML-файла
<?xml version = "1.0" encoding = "UTF-8"?>
<workers>
<worker>
<name>sam</name>
<batch_id>1345</batch_id>
<dependents>
<name>sara</name>
</dependents>
<dependents>
<name>tom</name>
</dependents>
<dependents>
<name>harry</name>
</dependents>
<locations>
<place>ny</place>
<type>work</type>
</locations>
<locations>
<place>sfo</place>
<type>home</type>
</locations>
</worker>
</workers>
Ожидаемый результат
<?xml version = "1.0" encoding = "UTF-8"?>
<workers>
<worker>
<name>sam</name>
<batch_id>1345</batch_id>
<dependents1>
<name>sara</name>
</dependents1>
<dependents_row1>1</dependents_row1>
<dependents2>
<name>tom</name>
</dependents2>
<dependents_row2>2</dependents_row2>
<dependents3>
<name>harry</name>
</dependents3>
<dependents_row3>3</dependents_row3>
<locations1>
<place>ny</place>
<type>work</type>
</locations1>
<locations_row1>1</locations_row1>
<locations2>
<place>sfo</place>
<type>home</type>
</locations2>
<locations_row2>2</locations_row2>
</worker>
</workers>
Что касается создания этого с помощью XSLT, используйте <xsl:number/>
с переменной для создания индекса и <xsl:element name = "{name()}{$variable}">
для создания элемента. Определить, есть ли у элемента братьев и сестер, немного сложнее, но использование ключа также позволяет, вам может потребоваться сообщить нам, можете ли вы работать с XSLT 2 или 3 или застряли с XSLT 1.
Это требование последующей системы. Они хотели получить XML-файл с уникальными именами элементов. В моем случае мне нужно сделать уникальными только пару одноуровневых узлов, поэтому нет необходимости проверять, есть ли у элемента одноуровневые узлы или нет (я использовал эти слова, чтобы лучше представить свой пример). Я даже могу написать условие соответствия шаблону, чтобы указать родственные узлы, которые необходимо сделать уникальными.
Я могу работать с xslt 2.0, спасибо Мартину за быстрый ответ. Не могли бы вы поделиться со мной кодом, который выполняет этот трюк? Я только новичок в XML / XSLT.
Вы можете использовать следующую таблицу стилей (несмотря на сомнения в дизайне):
<xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method = "xml" indent = "yes" />
<!-- identity template -->
<xsl:template match = "node()|@*">
<xsl:copy>
<xsl:apply-templates select = "node()|@*" />
</xsl:copy>
</xsl:template>
<xsl:template match = "/workers/worker"> <!-- handles the sub-elements of the worker node -->
<xsl:copy>
<xsl:apply-templates select = "*[not(self::dependents or self::locations)]" />
<xsl:apply-templates select = "dependents" />
<xsl:apply-templates select = "locations" />
</xsl:copy>
</xsl:template>
<xsl:template match = "dependents|locations"> <!-- handles the addition of the position nodes -->
<xsl:element name = "{concat(local-name(),position())}">
<xsl:copy-of select = "*" />
</xsl:element>
<xsl:element name = "{concat(local-name(),'_row',position())}">
<xsl:value-of select = "position()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Спасибо, zx485, я вижу, что это версия 1.0 ... Есть ли другой способ сделать это в версии 2.0?
@Anoop: Я только что обновил свой ответ. Он работает как с XSLT-1.0, так и с 2.0 или выше.
Спасибо zx485, тестирую.
Общее решение, которое не знает об именах элементов и использует xsl:number
, как предлагается в комментарии, -
<xsl:transform xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "2.0"
xmlns:xs = "http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes = "xs">
<xsl:output indent = "yes"/>
<xsl:strip-space elements = "*"/>
<xsl:template match = "@*|node()">
<xsl:copy>
<xsl:apply-templates select = "@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match = "*[some $sibling in (preceding-sibling::*, following-sibling::*) satisfies node-name($sibling) = node-name(.)]">
<xsl:variable name = "index" as = "xs:integer">
<xsl:number/>
</xsl:variable>
<xsl:element name = "{name()}{$index}">
<xsl:apply-templates/>
</xsl:element>
<xsl:element name = "{name()}_row{$index}">
<xsl:value-of select = "$index"/>
</xsl:element>
</xsl:template>
</xsl:transform>
Конечно, вы можете уменьшить или упростить match = "*[some $sibling in (preceding-sibling::*, following-sibling::*) satisfies node-name($sibling) = node-name(.)]"
, например, match = "descendents | locations"
, если элементы известны и / или нацелены только на определенные.
http://xsltransform.hikmatu.com/6qM2e2o/2
Спасибо, Мартин, я тоже это проверяю.
Чья идея - это дизайн мишени? Будет сложно обработать это с помощью большинства XML API элегантным и эффективным способом, если у вас есть имена элементов с добавленным индексом, поскольку ни DOM (например,
getElementsByTagName
), ни XSLT / XPath / XQuery (например,child::foo
) не имеют прямого способа выразить имя элемента переменной (в XSLT / XPath / XQuery можно использоватьchild::*[starts-with(local-name(), 'foo')]
, но это довольно громоздко). Как вы думаете, зачем вам нужен этот целевой дизайн?