#xml #sorting #xslt
#xml #сортировка #xslt
Вопрос:
Я пытаюсь отсортировать дочерние узлы на основе поля start_date. В приведенном ниже XML-узле <employment_information>
есть два дочерних узла <job_information>
с убывающей начальной датой, которая должна быть возрастающей.
После долгих поисков и попыток я добился того, что сортировка работает, но проблема, с которой я столкнулся, заключается в том, что другие поля <employment_information>
исчезают. Узлы <job_information>
расположены в правильном порядке, но другие поля исчезли.
Мой XML:
<queryCompoundEmployeeResponse>
<CompoundEmployee period_start="2020-09-30" period_end="2020-12-03">
<id>347</id>
<type>CompoundEmployee</type>
<swHire>Y</swHire>
<swRehire>N</swRehire>
<swRetire>N</swRetire>
<person>
<action>NO CHANGE</action>
<country_of_birth>NLD</country_of_birth>
<created_by>admin_nm</created_by>
<created_on_timestamp>2018-05-02T14:03:25.000Z</created_on_timestamp>
<person_id>347</person_id>
<person_id_external>10160</person_id_external>
<place_of_birth>aaa</place_of_birth>
<employment_information>
<action>NO CHANGE</action>
<assignment_class>ST</assignment_class>
<created_by>admin_nm</created_by>
<created_on_timestamp>2018-05-02T14:03:25.000Z</created_on_timestamp>
<direct_reports>12</direct_reports>
<employment_id>347</employment_id>
<hiringNotCompleted>false</hiringNotCompleted>
<isContingentWorker>false</isContingentWorker>
<jobNumber>1</jobNumber>
<last_modified_by>aaa</last_modified_by>
<last_modified_on>2019-09-05T10:38:50.000Z</last_modified_on>
<originalStartDate>1992-05-01</originalStartDate>
<serviceDate>1992-05-01</serviceDate>
<start_date>1992-05-01</start_date>
<user_id>10160</user_id>
<job_information>
<action>CHANGE</action>
<shift_factor>0.0</shift_factor>
<shift_rate>0.0</shift_rate>
<standard_hours>38.0</standard_hours>
<start_date>2020-10-10</start_date>
<time_recording_admissibility_code>NL</time_recording_admissibility_code>
<time_recording_profile_code>NL</time_recording_profile_code>
<time_recording_variant>DURATION</time_recording_variant>
<time_type_profile_code>NL20 /CI </time_type_profile_code>
<timezone>Europe/Amsterdam</timezone>
<workingDaysPerWeek>5.0</workingDaysPerWeek>
<workschedule_code>DUMMY</workschedule_code>
</job_information>
<job_information>
<shift_factor>0.0</shift_factor>
<shift_rate>0.0</shift_rate>
<standard_hours>0.0</standard_hours>
<start_date>2020-10-01</start_date>
<timezone>Europe/Amsterdam</timezone>
<workingDaysPerWeek>0.0</workingDaysPerWeek>
</job_information>
<job_event_information>
<action>INSERT</action>
<created_on_timestamp>2020-08-01T20:00:48.000Z</created_on_timestamp>
<event>5</event>
<event_date>2020-08-01</event_date>
<event_reason>DATACHG</event_reason>
<seq_number>1</seq_number>
</job_event_information>
</employment_information>
</person>
<execution_timestamp>2020-08-17T14:00:48.000Z</execution_timestamp>
<version_id>2005P0</version_id>
</CompoundEmployee>
</queryCompoundEmployeeResponse>
XSL, который я использую, это:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="queryCompoundEmployeeResponse/CompoundEmployee/person/employment_information">
<xsl:copy>
<xsl:apply-templates select="job_information">
<!-- concat year, month, day -->
<xsl:sort select="concat(
substring(start_date, 1, 4),
substring(start_date, 6, 2),
substring(start_date, 9, 2)
)" order="ascending" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Результат таков (выдержка)
Как вы можете видеть, сортировка в порядке, но другие дети из employment_information ушли. Как я могу их сохранить? Чего мне не хватает?
<employment_information>
<job_information>
<shift_factor>0.0</shift_factor>
<shift_rate>0.0</shift_rate>
<standard_hours>0.0</standard_hours>
<start_date>2020-10-01</start_date>
<timezone>Europe/Amsterdam</timezone>
<workingDaysPerWeek>0.0</workingDaysPerWeek>
</job_information>
<job_information>
<action>CHANGE</action>
<shift_factor>0.0</shift_factor>
<shift_rate>0.0</shift_rate>
<standard_hours>38.0</standard_hours>
<start_date>2020-10-10</start_date>
<time_recording_admissibility_code>NL</time_recording_admissibility_code>
<time_recording_profile_code>NL</time_recording_profile_code>
<time_recording_variant>DURATION</time_recording_variant>
<time_type_profile_code>NL20 /CI </time_type_profile_code>
<timezone>Europe/Amsterdam</timezone>
<workingDaysPerWeek>5.0</workingDaysPerWeek>
<workschedule_code>DUMMY</workschedule_code>
</job_information>
</employment_information>
Ответ №1:
Что вам нужно сделать, это скопировать другие узлы (дочерние <employment_information>
узлы, но не те, которые есть <job_information>
).
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="employment_information">
<xsl:copy>
<xsl:apply-templates select="node()[not(self::job_information)] | @*" />
<xsl:apply-templates select="job_information">
<!-- concat year, month, day -->
<xsl:sort select="concat(
substring(start_date, 1, 4),
substring(start_date, 6, 2),
substring(start_date, 9, 2)
)" data-type="number" order="ascending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Я использовал <xsl:apply-templates select="node()[not(self::job_information)] | @*" />
, потому что это запускает шаблон идентификации, который в конечном итоге скопирует эти узлы. Но в данном конкретном случае <xsl:copy-of select="node()[not(self::job_information)] | @*" />
это сработает так же хорошо.
Комментарий Майкла верен — это печальное последствие того, что явно не указан желаемый дочерний порядок. Чтобы сохранить исходный порядок элементов, вы могли бы настроить таргетинг на внутренний <xsl:apply-templates>
с более высокой степенью детализации, например
<!-- 1. all child nodes that come before the <job_information> block -->
<xsl:apply-templates select="node()[not(self::job_information) and following-sibling::job_information]" />
<!-- 2. the <job_information> block itself -->
<xsl:apply-templates select="job_information">
<xsl:sort ... />
</xsl:apply-templates>
<!-- 3. all child nodes that come after the <job_information> block -->
<xsl:apply-templates select="node()[not(self::job_information) and preceding-sibling::job_information]" />
Но этот подход требует, чтобы <job_information>
элементы гарантированно были блоком без посторонних элементов между ними. В конце концов, решение заключается в том, ломается ли что-либо, когда другие узлы меняют позиции, или этого достаточно для того, чтобы ваш вариант использования <job_information>
отображался в правильном порядке.
Комментарии:
1. @Tom64 Обратите внимание, что это переместит
job_event_information
и разместит его перед всемиjob_information
узлами. Если это приемлемо, то это должно хорошо работать для вас. — Кроме того, вы могли бы сделать просто<xsl:sort select="start_date"/>
вместо всего этого джаза.2. Действительно, job_event_information переместился из-за более раннего применения шаблона, но это не проблема.
3. И мне не нужен был весь этот джаз 🙂