Зацикливание и разделение дочерних узлов на несколько дочерних узлов - PullRequest
0 голосов
/ 20 февраля 2019

Вот мой входной XML, который мне нужно преобразовать с использованием XSLT 2.0

<?xml version="1.0" encoding="UTF-8"?>
<Workers>
    <Worker>
        <id>1234</id>
        <loc>New York</loc>
        <Days>1</Days>
        <StartDate>2019-01-26</StartDate>
    </Worker>
    <Worker>
        <id>2345</id>
        <loc>Boston</loc>
        <Batch>A</Batch>
        <Days>3</Days>
        <Units>2</Units>
        <StartDate>2019-02-01</StartDate>
    </Worker>
</Workers>

Токовый выход

<?xml version="1.0" encoding="UTF-8"?>
<Workers>
    <Worker>
        <id>1234</id>
        <loc>New York</loc>
        <Days>1</Days>
        <StartDate>2019-01-26</StartDate>
    </Worker>
    <Worker>
        <id>2345</id>
        <loc>Boston</loc>
        <RecordNumber>1</RecordNumber>
        <WorkerDays>1</WorkerDays>
        <StartDate>2019-02-02</StartDate>
    </Worker>
    <Worker>
        <id>2345</id>
        <loc>Boston</loc>
        <RecordNumber>2</RecordNumber>
        <WorkerDays>1</WorkerDays>
        <StartDate>2019-02-03</StartDate>
    </Worker>
    <Worker>
        <id>2345</id>
        <loc>Boston</loc>
        <RecordNumber>3</RecordNumber>
        <WorkerDays>1</WorkerDays>
        <StartDate>2019-02-04</StartDate>
    </Worker>
</Workers>

Ожидаемый вывод

<?xml version="1.0" encoding="UTF-8"?>
<Workers>
    <Worker>
        <id>1234</id>
        <loc>New York</loc>
        <Days>1</Days>
        <StartDate>2019-01-26</StartDate>
    </Worker>
    <Worker>
        <id>2345</id>
        <loc>Boston</loc>
        <RecordNumber>1</RecordNumber>
        <WorkerDays>1</WorkerDays>
        <StartDate>2019-02-01</StartDate>
    </Worker>
    <Worker>
        <id>2345</id>
        <loc>Boston</loc>
        <RecordNumber>2</RecordNumber>
        <WorkerDays>1</WorkerDays>
        <StartDate>2019-02-02/StartDate>
    </Worker>
    <Worker>
        <id>2345</id>
        <loc>Boston</loc>
        <RecordNumber>3</RecordNumber>
        <WorkerDays>1</WorkerDays>
        <StartDate>2019-02-03</StartDate>
    </Worker>
    <Worker>
        <id>2345</id>
        <loc>Boston</loc>
        <RecordNumber>1</RecordNumber>
        <WorkerDays>1</WorkerDays>
        <StartDate>2019-02-01</StartDate>
    </Worker>
    <Worker>
        <id>2345</id>
        <loc>Boston</loc>
        <RecordNumber>2</RecordNumber>
        <WorkerDays>1</WorkerDays>
        <StartDate>2019-02-02/StartDate>
    </Worker>
</Workers>

Мои требования

a.) Если элемент <Batch> отсутствует в дочернем узле, этот дочерний узел должен быть скопирован так, как он отображается в XML.В приведенном выше XML первый рабочий дочерний узел должен быть скопирован так, как он выглядит в XML, так как в нем нет элемента <Batch>.

b.) Если элемент <Batch> присутствует вдочерний узел, этот дочерний узел должен быть разбит на несколько дочерних узлов на основе следующих двух условий

1.) Количество дочерних узлов необходимо создавать столько раз, сколько значение элемента <Days>.В этом случае <Days> имеет 3 в качестве значения, поэтому необходимо создать 3 дочерних узла, и каждый из этих дочерних узлов должен увеличить <StartDate> на единицу и создать новый элемент <RecordNumber>, который должен содержать значениеэтот цикл.

2.) Дочерний узел снова должен быть разделен на основе значения элемента <Units>.В приведенном выше XML <Units> равно 2, поэтому необходимо создать дочерний узел два раза, а <StartDate> нужно увеличивать на единицу каждый раз, когда он создает, и создать новый элемент <RecordNumber>, который должен содержать значение этого цикла.

Текущий XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:functx="http://www.functx.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all">
    <xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Workers/Worker[exists(Batch)]">
        <xsl:variable name="start" select="1"/>
        <xsl:variable name="counter" select="Days"/>
        <xsl:variable name="Records" select="."/>
        <xsl:for-each select="$start to $counter">
            <xsl:apply-templates select="$Records" mode="replicate">
                <xsl:with-param name="data" select="."/>
            </xsl:apply-templates>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="Workers/Worker" mode="replicate">
        <xsl:param name="data"/>
        <Worker>
            <id>
                <xsl:value-of select="id"/>
            </id>
            <loc>
                <xsl:value-of select="loc"/>
            </loc>
            <RecordNumber>
                <xsl:value-of select="$data"/>
            </RecordNumber>
            <WorkerDays>1</WorkerDays>
            <StartDate>
                <xsl:value-of select="xs:date(StartDate) + xs:dayTimeDuration('P1D') * $data"/>
            </StartDate>
        </Worker>
    </xsl:template>
</xsl:stylesheet>

Проблемы:

  1. Значение <StartDate> неверно - похоже, что фактическое <StartDate> отсутствует

  2. XSLT вообще не разделяет дочерний узел.

  3. <Worker> дочерний узел должен появляться 6 раз в ожидаемом выводе, тогда как сейчас я получаю только 4.

Может кто-нибудь помочь мне исправитьвопрос пожалуйста?

Спасибо

1 Ответ

0 голосов
/ 20 февраля 2019

Из вашего ожидаемого вывода я вижу, что вам нужно 2 цикла в последовательности :

  • первый - выполненный Days количество раз,
  • второй - выполняется Units число раз,

для последовательных дат, начиная с даты ввода.

Обратите внимание также, что в for-each выполняется цикл элемент изменен, поэтому в случае обоих циклов for-each элемент контекста является текущим числом выполнения, взятым из атрибута select.

Это причина того, что в каждом цикле, когдавызывающий шаблон Worker, параметр dayNo задан как точка , а currElem - это просто элемент, для которого был вызван шаблон Worker[Batch].

Что касается вывода StartDate, то dayTimeDuration необходимо добавлять $dayNo - 1 количество раз.

Другие полезные дополнения / изменения:

  • <xsl:strip-space elements="*"/>в начале, вызывая «лучший» отступ для вывода.
  • <xsl:sequence .../> - более короткая форма для копирования исходного элементатс к выходу.Также стоит отметить, что он работает намного быстрее и потребляет гораздо меньше памяти.
  • Я удалил некоторые переменные, использованные только 1 раз.
  • Вы использовали переменную с именем data.ИМХО это имя "слишком общее", поэтому я изменил его на currElem (текущий элемент).

Так что весь скрипт может выглядеть следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all">
  <xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="Worker[Batch]">
    <xsl:variable name="currElem" select="."/>
    <xsl:for-each select="1 to Days">
      <xsl:apply-templates select="$currElem" mode="replicate">
        <xsl:with-param name="dayNo" select="."/>
      </xsl:apply-templates>
    </xsl:for-each>
    <xsl:for-each select="1 to Units">
      <xsl:apply-templates select="$currElem" mode="replicate">
        <xsl:with-param name="dayNo" select="."/>
      </xsl:apply-templates>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match="Worker" mode="replicate">
    <xsl:param name="dayNo"/>
    <Worker>
      <xsl:sequence select="id, loc"/>
      <RecordNumber><xsl:value-of select="$dayNo"/></RecordNumber>
      <WorkerDays>1</WorkerDays>
      <StartDate>
        <xsl:value-of select="xs:date(StartDate) + xs:dayTimeDuration('P1D') * ($dayNo - 1)"/>
      </StartDate>
    </Worker>
  </xsl:template>

  <xsl:template match="@*|node()">
    <xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Рабочий пример см. http://xsltransform.net/asnmyP/1

Первая версия находится под http://xsltransform.net/asnmyP (если вы хотите сравнить их).

...