Ограничьте рекурсию одним глубоким l oop и назначьте точный идентификатор всем элементам - PullRequest
0 голосов
/ 26 марта 2020

Необходимо заказать элементы из смешанного заказа. Заказ осуществляется с использованием метода рекурсии. Необходимо выполнить два условия, которые пока не реализованы в коде:

  • рекурсия должна охватывать только самую длинную цепочку. Другие цепочки не должны выдаваться
  • элемент с STATUS '0' (логически - самый первый элемент в цепочке) не должен отображаться
  • , но его значение id-3 должно быть назначено всем другие последующие элементы (с STATUS='1' в цепочке (в качестве дополнительного поля с общим идентификатором).

1-источник

<root>

  <object id-1="aaaa" parent-id="bbbb" id-3="COMMON-ID-1"  STATUS="0" add-1="value" add-2="value"/>
  <object id-1="1111" parent-id="2222" id-3="COMMON-ID-2"  STATUS="0" add-1="value" add-2="value"/>

  <object id-1="bbbb" parent-id="cccc" id-3="value"        STATUS="1" add-1="value" add-2="value"/>
  <object id-1="2222" parent-id="3333" id-3="value"        STATUS="1" add-1="value" add-2="value"/>
  <object id-1="cccc" parent-id="dddd" id-3="value"        STATUS="1" add-1="value" add-2="value"/>
  <object id-1="3333" parent-id="4444" id-3="value"        STATUS="1" add-1="value" add-2="value"/>
  <object id-1="dddd" parent-id="eeee" id-3="value"        STATUS="1" add-1="value" add-2="value"/>
  <object id-1="4444" parent-id="5555" id-3="value"        STATUS="1" add-1="value" add-2="value"/>

</root>

2 - присутствует XSLT (производит все рекурсии, хотя требуется только одна, общий идентификатор не назначается)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:exslt="http://exslt.org/common">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

  <xsl:key name="by-id" match="object" use="@id-1"/>

  <xsl:template match="object" mode="ancestors">
    <xsl:copy>
      <xsl:copy-of select="@*"/>

      <xsl:attribute name="COMMON-ID">
        <xsl:value-of select="@id-3[@STATUS='0']"/>
      </xsl:attribute>

      <xsl:apply-templates select="key('by-id', @parent-id)" mode="ancestors"/>
    </xsl:copy>
  </xsl:template>


  <xsl:template match="/">
    <root>
      <xsl:apply-templates select="root/object[@STATUS='1']" mode="ancestors"/>
    </root>
  </xsl:template>

</xsl:stylesheet>

3-output

<root>

  <object id-1="bbbb" id-2="cccc" id-3="value" STATUS="1" add-1="value" add-2="value" COMMON-ID="COMMON-ID-1"/> <!-- chain 1-->
  <object id-1="cccc" id-2="dddd" id-3="value" STATUS="1" add-1="value" add-2="value" COMMON-ID="COMMON-ID-1"/>
  <object id-1="dddd" id-2="eeee" id-3="value" STATUS="1" add-1="value" add-2="value" COMMON-ID="COMMON-ID-1"/>

  <object id-1="2222" id-2="3333" id-3="value"  STATUS="1" add-1="value" add-2="value" COMMON-ID="COMMON-ID-2"/>  <!-- chain 1-->
  <object id-1="3333" id-2="4444" id-3="value"  STATUS="1" add-1="value" add-2="value" COMMON-ID="COMMON-ID-2"/>
  <object id-1="4444" id-2="5555" id-3="value"  STATUS="1" add-1="value" add-2="value" COMMON-ID="COMMON-ID-2"/>

</root>

Ответы [ 2 ]

1 голос
/ 27 марта 2020

Только как выбрать самую длинную цепочку?

Рассмотрим следующий упрощенный пример .

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

XML

<root>
    <object id="a"/>
    <object id="b"/>
    <object id="c"/>
    <object id="aa" parent-id="a"/>
    <object id="bb" parent-id="b"/>
    <object id="cc" parent-id="c"/>
    <object id="aaa" parent-id="aa"/>
    <object id="bbb" parent-id="bb"/>
    <object id="ccc" parent-id="cc"/>
    <object id="bbbb" parent-id="bbb"/>
    <object id="cccc" parent-id="ccc"/>
    <object id="bbbbb" parent-id="bbbb"/>
</root>

XSLT 1.0 (+ функция установки узла)

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="child" match="object" use="@parent-id" />

<xsl:template match="/root">
    <!-- generate chains -->
    <xsl:variable name="chains">
        <xsl:apply-templates select="object[not(@parent-id)]"/>
    </xsl:variable>
    <!-- find the longest chain -->
    <xsl:for-each select="exsl:node-set($chains)/object">
        <xsl:sort select="count(descendant::object)" data-type="number" order="descending"/>
        <xsl:if test="position() =1 ">
            <xsl:copy-of select="."/>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

<xsl:template match="object">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates select="key('child', @id)"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

После первого прохода переменная $chains будет содержать:

<object id="a">
  <object id="aa" parent-id="a">
    <object id="aaa" parent-id="aa"/>
  </object>
</object>
<object id="b">
  <object id="bb" parent-id="b">
    <object id="bbb" parent-id="bb">
      <object id="bbbb" parent-id="bbb">
        <object id="bbbbb" parent-id="bbbb"/>
      </object>
    </object>
  </object>
</object>
<object id="c">
  <object id="cc" parent-id="c">
    <object id="ccc" parent-id="cc">
      <object id="cccc" parent-id="ccc"/>
    </object>
  </object>
</object>

После сортировки цепочек по их длине (т.е. количеству объектов-потомков) и выбора самой длинной из них мы получим:

Результат

<?xml version="1.0" encoding="UTF-8"?>
<object id="b">
  <object id="bb" parent-id="b">
    <object id="bbb" parent-id="bb">
      <object id="bbbb" parent-id="bbb">
        <object id="bbbbb" parent-id="bbbb"/>
      </object>
    </object>
  </object>
</object>

Подсказка : с рекурсией, работающей вниз, очень легко использовать параметр шаблона для передачи общего значения от предка всем его потомкам.

1 голос
/ 27 марта 2020

Это изменение вашего root шаблона. Ваши другие шаблоны остаются прежними. Надеюсь, это поможет.

  <xsl:template match="/">
    <xsl:variable name="chains">
      <xsl:apply-templates select="root/object[@STATUS='1']" mode="ancestors"/>
    </xsl:variable>

    <xsl:variable name="chainList" select="exslt:node-set($chains)"/>

    <xsl:variable name="objects">
      <xsl:for-each select="$chainList/object">
        <element>
          <position>
            <xsl:value-of select="position()"/>
          </position>
          <numberOfObjects>
            <xsl:value-of select="count(.//object)+1"/>
          </numberOfObjects>
        </element>       
      </xsl:for-each>
    </xsl:variable>

    <xsl:variable name="objectList" select="exslt:node-set($objects)"/>

    <xsl:variable name="sortedObjects">
      <xsl:for-each select="$objectList/element">
        <xsl:sort select="numberOfObjects" order="descending" data-type="number"/>
        <xsl:copy-of select="."/>
      </xsl:for-each>
    </xsl:variable>

    <xsl:variable name="sortedObjectList" select="exslt:node-set($sortedObjects)"/>

    <xsl:variable name="maxObjects" select="$sortedObjectList/element[1]/numberOfObjects"/>

    <root>
      <xsl:for-each select="$objectList/element[numberOfObjects = $maxObjects]">
        <xsl:variable name="position" select="position"/>

        <xsl:copy-of select="$chainList/object[number($position)]"/>

      </xsl:for-each>
    </root>
  </xsl:template>
...