Хобрук: Ваш путь к мастерству в программировании

Дублированный родительский элемент XSL для каждого вхождения дочернего элемента

Мне нужно дублировать родительский элемент для каждого экземпляра дочернего элемента, но я не хочу включать братьев и сестер дочернего элемента в вывод. Затем я хотел бы отсортировать родителей на основе поля в дочернем узле.

Пример ввода:

<Customer>
  <a/>
  <b/>
  <Market>
    <MarketNumber>100</MarketNumber>
  </Market>
  <Market>
    <MarketNumber>200</MarketNumber>
  </Market>
  <c/>
</Customer>
<Customer>
  <a/>
  <b/>
  <Market>
    <MarketNumber>100</MarketNumber>
  </Market>
  <c/>
</Customer>

Желаемый результат (3 клиента с одним рынком каждый):

<Customer>
  <a/>
  <b/>
  <Market>
    <MarketNumber>100</MarketNumber>
  </Market>
  <c/>
</Customer>
<Customer>
  <a/>
  <b/>
  <Market>
    <MarketNumber>100</MarketNumber>
  </Market>
  <c/>
</Customer>
<Customer>
  <a/>
  <b/>
  <Market>
    <MarketNumber>200</MarketNumber>
  </Market>
  <c/>
</Customer>

Я решил это, выбрав все рынки и скопировав его родительские элементы вручную, но хотел бы сделать это, не называя их (его 33 элемента в родительском узле). Проблема в том, что я не могу выбрать "../." потому что тогда я также копирую рынок братьев и сестер.

<xsl:apply-templates select="Customer/Market">
    <xsl:sort select="MarketNumber" data-type="number" order="ascending"/>
</xsl:apply-templates>
...
<xsl:template match="Market">
    <!--Duplicate all customer data for each market-->
    <Customer>
        <xsl:copy-of select="../a"/>
        <xsl:copy-of select="../b"/>
        <xsl:copy-of select="."/> <!--Current market -->
        <xsl:copy-of select="../c"/>
    </Customer>
</xsl:template>

Я подозреваю, что это можно решить, скопировав родительский элемент, кроме Market, а затем скопировав Market с "." как я делаю выше, но я не могу понять, как .. Любые подсказки приветствуются! Другие решения также приветствуются, но они также должны обрабатывать сортировку.

Спасибо! Ричард

01.12.2016

  • Я решил эту проблему, выбрав все рынки и вручную скопировав его родительские элементы - вы имеете в виду копирование его элементов sibling вручную? В противном случае я не понимаю, как это связано с показанным вами XSLT. 01.12.2016
  • Кроме того, имеет значение, доступен ли вам XSLT 2.0 или вы застряли на 1.0. Что вы используете? 01.12.2016

Ответы:


1
<xsl:apply-templates select="Customer/Market">
    <xsl:sort select="MarketNumber" data-type="number" order="ascending"/>
</xsl:apply-templates>
...
<xsl:template match="Market">
    <!--Duplicate all customer data for each market-->
    <Customer>
        <xsl:copy-of select="../*[not(self::Market)] | ."/>
    </Customer>
</xsl:template>

Это скопирует все элементы того же уровня, которые не являются элементами Market, а также скопирует текущий (совпадающий) элемент Market. Это то, что вы хотели?

01.12.2016
  • Спасибо, Ларс! Он работает так, как ожидалось, и я могу изменять имена элементов или добавлять новые элементы без необходимости обновлять XSL. 05.12.2016
  • @RichardL: рад, что это помогло. Не забудьте принять ответ (нажмите на галочку), если это решило вашу проблему. 05.12.2016
  • Ах, спасибо, попытался проголосовать за это, но у меня была слишком низкая репутация ... до сих пор не видел галочку :) 06.12.2016
  • У меня есть другое сопоставление, которое немного сложнее в том смысле, что номер рынка вложен на один уровень глубже, как это '‹Клиент›‹x/›‹Рынки›‹Рынок›200‹/Рынок›‹Рынок›101‹/Рынок›‹ /Рынки›‹y/›‹/Клиент›». Мне по-прежнему нужен только один рынок для каждого клиента, но если я адаптирую ваш пример с помощью '‹xsl:copy-of select=../../*[not(self::Markets/Market)] | .././›' Я получаю двух клиентов, но с обоими рынками 06.12.2016
  • @RichardL: Возможно, вам придется опубликовать новый вопрос для этого. Решение достаточно сложное, и я не думаю, что у меня есть время, чтобы работать над ним в ближайшие несколько часов (хотел бы я это сделать!) P.S. Спасибо за согласие. 06.12.2016

  • 2

    Я опоздал на вечеринку на несколько лет, но у меня было это требование. Решение, которое я придумал, состояло в том, чтобы использовать именованные шаблоны для рекурсивного копирования XML-дерева, исключая имена элементов, которые я не хотел копировать, и используя функцию generate-id для идентификации узла, который я все еще хотел скопировать. См. комментарии в приведенном ниже коде для более подробной информации.

    Это решение работает в XSLT 1.0.

    Пример XSLT Fiddle доступен здесь: https://xsltfiddle.liberty-development.net/bnnZWm.

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet 
      version="1.0" 
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
      exclude-result-prefixes="msxsl"
    >
      <xsl:output method="xml" indent="yes"/>
    
      <!-- copy everything as is, unless told otherwise-->
      <xsl:template match="@* | node()">
        <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
      </xsl:template>
    
      <!-- 
      match on the document element
      ensure there's a single root element, Customers
      match on any Market nodes, so we get a copy of everything for that Market's customer (as per the Market template) with that Market
      OR match any customer that doesn't have any markets; so we don't exclude those from the results (did you want that; if not switch the apply-templates for the commented out version below)
      -->
      <xsl:template match="/">
        <Customers>
          <xsl:apply-templates select="//Market|//Customer[not(./Markets/Market)]" />
          <!-- <xsl:apply-templates select="//Market" /> -->
        </Customers>
      </xsl:template>
    
      <!-- 
      For the markets matched above, call our copyAllButMe template
      set the rootNode (the node to be copied) to the current Market's ancestor Customer element
      set the ignoreElementName to the current element's name (i.e. so when copying the customer, we don't copy any child Market elements
      set the includeNodeId to the current element's id (i.e. so when ignoring other Markets, we don't ignore the current element)
      -->
      <xsl:template match="Market">
        <xsl:call-template name="copyAllButMe">
          <xsl:with-param name="rootNode" select="../.." />
          <xsl:with-param name="ignoreElementName" select="local-name()" />
          <xsl:with-param name="includeNodeId" select="generate-id()" />
        </xsl:call-template>
      </xsl:template>
    
      <!--
      Test: 
        Is the current element's name a name we should be ignoring?  
        If it is, does this element's ID match the ID we want to include?
      If we're not ignoring this element name, or the id is the one to include then we proceed:
      Copy the element's name
      Copy any attrinbutes of thie element
      Loop through all child nodes of this element
      - If the child node is itself an element, call this template (i.e. recursion)
      - Otherwise (e.g. it' text node, comment node, etc), copy the node as-is
      -->
      <xsl:template name="copyAllButMe">
        <xsl:param name="rootNode" />
        <xsl:param name="ignoreElementName" />
        <xsl:param name="includeNodeId" />
        <xsl:if test="(local-name($rootNode) != $ignoreElementName) or (generate-id($rootNode) = $includeNodeId)">
          <xsl:element name="{local-name($rootNode)}" namespace="{namespace-uri($rootNode)}">
            <xsl:copy-of select="$rootNode/attribute::*" />
            <xsl:for-each select="$rootNode/child::node()">
              <xsl:choose>
                <xsl:when test="self::*">
                  <xsl:call-template name="copyAllButMe">
                    <xsl:with-param name="rootNode" select="self::node()" />
                    <xsl:with-param name="ignoreElementName" select="$ignoreElementName" />
                    <xsl:with-param name="includeNodeId" select="$includeNodeId" />
                  </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:copy-of select="self::node()" />
                </xsl:otherwise>
              </xsl:choose>
            </xsl:for-each>
          </xsl:element>
        </xsl:if>
      </xsl:template>
    
    </xsl:stylesheet>
    
    18.04.2019
    Новые материалы

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

    Внедрите OAuth в свои веб-приложения для повышения безопасности
    OAuth — это широко распространенный стандарт авторизации, который позволяет приложениям получать доступ к ресурсам от имени пользователя, не раскрывая его пароль. Это позволяет пользователям..

    Классы в JavaScript
    class является образцом java Script Object. Конструкция «class» позволяет определять классы на основе прототипов с чистым, красивым синтаксисом. // define class Human class Human {..

    Как свинг-трейдеры могут использовать ИИ для больших выигрышей
    По мере того как все больше и больше профессиональных трейдеров и активных розничных трейдеров узнают о возможностях, которые предоставляет искусственный интеллект и машинное обучение для улучшения..

    Как построить любой стол
    Я разработчик программного обеспечения. Я люблю делать вещи и всегда любил. Для меня программирование всегда было способом создавать вещи, используя только компьютер и мое воображение...

    Обзор: Машинное обучение: классификация
    Только что закончил третий курс курса 4 часть специализации по машинному обучению . Как и второй курс, он был посвящен низкоуровневой работе алгоритмов машинного обучения. Что касается..

    Разработка расширений Qlik Sense с qExt
    Использование современных инструментов веб-разработки для разработки крутых расширений Вы когда-нибудь хотели кнопку для установки переменной в приложении Qlik Sense? Когда-нибудь просили..