Как получить атрибут последнего дочернего элемента, который не соответствует атрибуту родительского элемента (xslt)?

Я пытаюсь получить последний дочерний элемент для родителя, у которого нет атрибута, соответствующего одному из атрибутов родительского элемента? Я работал над этим какое-то время и до сих пор пришел с пустыми руками.

Я могу легко получить последний элемент и посмотреть, соответствует ли его атрибут. Если это не так, я могу проверить last-1, но как мне сделать это рекурсивно или с помощью встроенной функции?

В приведенном ниже примере родительский элемент (MainRecord) имеет 3 атрибута (Data1, Data2, Data3), которые могут присутствовать или быть нулевыми. Дочерний элемент имеет один атрибут данных, который может быть или не быть одним из родительских атрибутов данных.

Итак, для RecordNumber=1 мне нужно Cloud, потому что Rainbow находится в родительском элементе. Запись 3 восходит к Солнечному свету, потому что Цветок, Облако и Радуга находятся в родительском элементе.

XML-файл:

<MainRecord Data1="" Data2="" Data3="Rainbow" RecordNumber="1">
    <AuditRecord Data="Sky" SomomethingElse="Hi"/>
    <AuditRecord Data="Sunshine" SomomethingElse="Yo"/>
    <AuditRecord Data="Flower" SomomethingElse="Do"/>
    <AuditRecord Data="Cloud" SomomethingElse="Re"/>
    <AuditRecord Data="Rainbow" SomomethingElse="Mi"/>
</MainRecord>
<MainRecord Data1="" Data2="Cloud" Data3="Rainbow" RecordNumber="2">
    <AuditRecord Data="Sky" SomomethingElse="Hi"/>
    <AuditRecord Data="Sunshine" SomomethingElse="Yo"/>
    <AuditRecord Data="Flower" SomomethingElse="Do"/>
    <AuditRecord Data="Cloud" SomomethingElse="Re"/>
    <AuditRecord Data="Rainbow" SomomethingElse="Mi"/>
</MainRecord>
<MainRecord Data1="Flower" Data2="Cloud" Data3="Rainbow" RecordNumber="3">
    <AuditRecord Data="Sky" SomomethingElse="Hi"/>
    <AuditRecord Data="Sunshine" SomomethingElse="Yo"/>
    <AuditRecord Data="Flower" SomomethingElse="Do"/>
    <AuditRecord Data="Cloud" SomomethingElse="Re"/>
    <AuditRecord Data="Rainbow" SomomethingElse="Mi"/>
</MainRecord>
<MainRecord Data1="Cloud" Data2="Flower" Data3="" RecordNumber="4">
    <AuditRecord Data="Sky" SomomethingElse="Hi"/>
    <AuditRecord Data="Sunshine" SomomethingElse="Yo"/>
    <AuditRecord Data="Flower" SomomethingElse="Do"/>
    <AuditRecord Data="Cloud" SomomethingElse="Re"/>
    <AuditRecord Data="Rainbow" SomomethingElse="Mi"/>
</MainRecord>
<MainRecord Data1="" Data2="" Data3="" RecordNumber="5">
    <AuditRecord Data="Sky" SomomethingElse="Hi"/>
    <AuditRecord Data="Sunshine" SomomethingElse="Yo"/>
    <AuditRecord Data="Flower" SomomethingElse="Do"/>
    <AuditRecord Data="Cloud" SomomethingElse="Re"/>
    <AuditRecord Data="Rainbow" SomomethingElse="Mi"/>
</MainRecord>

Желаемый результат после трансформации:

<MainRecord RecordNumber="1" MaxData="Cloud"/>
<MainRecord RecordNumber="2" MaxData="Flower"/>
<MainRecord RecordNumber="3" MaxData="Sunshine"/>
<MainRecord RecordNumber="4" MaxData="Rainbow"/>
<MainRecord RecordNumber="5" MaxData="Rainbow"/>

Вот моя игра с этим. Переменная для maxData не работает, и я уверен, что это неправильный способ сделать это, но я застрял.

<MainRecords>
    <xsl:for-each select="MainRecord">
        <xsl:attribute name="RecordNumber"><xsl:value-of select="@RecordNumber"/></xsl:attribute>
        <xsl:variable name="maxData"/>
        <xsl:for-each select="AuditRecord">
            <xsl:if test="not(@Data=@Data1) and not(@Data=@Data2) and not(@Data=@Data3)">
                <xsl:variable name="maxDate"><xsl:value-of select="@Data"/></xsl:variable>
            </xsl:if>
        </xsl:for-each>

        <xsl:attribute name="Count"><xsl:value-of select="count(AuditRecord)"/></xsl:attribute>
        <xsl:attribute name="LastDataThatDoesntMatch"><xsl:value-of select="$maxDate"/></xsl:attribute>
        <xsl:attribute name="Last"><xsl:value-of select="ItemAudit[last()]/@Date"/></xsl:attribute>
        <xsl:attribute name="2ndLast"><xsl:value-of select="ItemAudit[last()-1]/@Date"/></xsl:attribute>
        <xsl:attribute name="3rdLast"><xsl:value-of select="ItemAudit[last()-2]/@Date"/></xsl:attribute>
        <xsl:attribute name="4thLast"><xsl:value-of select="ItemAudit[last()-3]/@Date"/></xsl:attribute>
    </xsl:for-each>
</MainRecords>

Я ценю любую помощь. Очевидно, что это не настоящие данные, а всего лишь простая версия задачи, не имеющая собственных данных. Не школьный проект, и я впервые использую xslt в проекте, так что это немного чуждо мне.


person Charistine    schedule 15.08.2019    source источник


Ответы (1)


Я думаю, ты просто хочешь

  <xsl:template match="MainRecord">
      <MainRecord RecordNumber="{@RecordNumber}" MaxData="{AuditRecord[not(@Data = current()/@*[starts-with(local-name(), 'Data')])][last()]/@Data}"/>
  </xsl:template>

https://xsltfiddle.liberty-development.net/bnnZXc

person Martin Honnen    schedule 15.08.2019
comment
Это именно то, что мне было нужно. Я не знаю, что я когда-нибудь придумал бы это, так что большое вам спасибо. Единственное, что мне нужно было настроить, это начало с, потому что имена атрибутов не были такими красивыми и нормализованными в моем приложении, как в моем примере. Мне пришлось изменить not(@Data = current()/@*[starts-with(local-name(), 'Data')]) на: not(@Data = current()/@*[' ABC']) или нет(@Data = current()/@*['XYZ']) или нет(@Data = current()/@*['QRS']) - person Charistine; 15.08.2019
comment
Я не совсем уверен, как называются ваши реальные атрибуты, но обратите внимание, что выражение, подобное @*['ABC'], просто выбирает все атрибуты, поскольку @* является подстановочным знаком для выбора всех атрибутов, и что любой предикат, добавленный в виде непустой строки (например, @*['ABC']), делает ничего не менять, потому что предикат с непустой строкой считается предикатом со значением true. - person Martin Honnen; 16.08.2019
comment
Вы очень правы конечно. Я понял это в тестировании и играл с этим. Похоже, это работает: не(@Data = current()/@ABC) и не(@Data = current()/@XYZ) и не(@Data = current()/@QRS) - person Charistine; 16.08.2019