Как добавить номер позиции узла в качестве элемента в XML с помощью XSLT?

У меня есть следующий XML-код, который необходимо преобразовать, как указано в разделе «Ожидаемый результат», приведенном ниже.

Требования к трансформации:

  1. Родственные узлы, такие как зависимые узлы и местоположения, должны быть добавлены с их соответствующими идентификаторами позиций ... например ... если в файле существует несколько зависимых узлов, узлы должны отображаться как и т. д.
  2. Положение родственных узлов необходимо вставить как отдельные имена элементов под их соответствующими дочерними узлами.

Пример входного 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>

Чья идея - это дизайн мишени? Будет сложно обработать это с помощью большинства XML API элегантным и эффективным способом, если у вас есть имена элементов с добавленным индексом, поскольку ни DOM (например, getElementsByTagName), ни XSLT / XPath / XQuery (например, child::foo) не имеют прямого способа выразить имя элемента переменной (в XSLT / XPath / XQuery можно использовать child::*[starts-with(local-name(), 'foo')], но это довольно громоздко). Как вы думаете, зачем вам нужен этот целевой дизайн?

Martin Honnen 08.08.2018 22:19

Что касается создания этого с помощью XSLT, используйте <xsl:number/> с переменной для создания индекса и <xsl:element name = "{name()}{$variable}"> для создания элемента. Определить, есть ли у элемента братьев и сестер, немного сложнее, но использование ключа также позволяет, вам может потребоваться сообщить нам, можете ли вы работать с XSLT 2 или 3 или застряли с XSLT 1.

Martin Honnen 08.08.2018 22:20

Это требование последующей системы. Они хотели получить XML-файл с уникальными именами элементов. В моем случае мне нужно сделать уникальными только пару одноуровневых узлов, поэтому нет необходимости проверять, есть ли у элемента одноуровневые узлы или нет (я использовал эти слова, чтобы лучше представить свой пример). Я даже могу написать условие соответствия шаблону, чтобы указать родственные узлы, которые необходимо сделать уникальными.

KM6895 08.08.2018 22:27

Я могу работать с xslt 2.0, спасибо Мартину за быстрый ответ. Не могли бы вы поделиться со мной кодом, который выполняет этот трюк? Я только новичок в XML / XSLT.

KM6895 08.08.2018 22:28
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
4
731
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Вы можете использовать следующую таблицу стилей (несмотря на сомнения в дизайне):

<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?

KM6895 08.08.2018 22:30

@Anoop: Я только что обновил свой ответ. Он работает как с XSLT-1.0, так и с 2.0 или выше.

zx485 08.08.2018 22:32

Спасибо zx485, тестирую.

KM6895 08.08.2018 22:54

Общее решение, которое не знает об именах элементов и использует 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

Спасибо, Мартин, я тоже это проверяю.

KM6895 08.08.2018 22:55

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