Объединить узлы XML с одинаковым именем с «_LIST» в имени узла, а также на корневом уровне - PullRequest
0 голосов
/ 11 октября 2018

Ниже приведен входной XML, и я ищу нужный вывод -

   <xml>
    <a>
        <element0>987</element0>
    </a>
    <a>
        <a_list_one>
            <a_lag_one>
                <element1>123</element1>
                <element2>456</element2>
            </a_lag_one>
        </a_list_one>
        <a_list_one>
            <a_lag_one>
                <element1>789</element1>
                <element2>678</element2>
            </a_lag_one>                
        </a_list_one>
        <a_list_two>
            <a_lag_two>
                <a_list_three>
                    <a_lag_three>
                        <element3>570</element3>
                        <element4>678</element4>
                    </a_lag_three>
                </a_list_three>
                <a_list_three>
                    <a_lag_three>
                        <element3>989</element3>
                        <element4>231</element4>
                    </a_lag_three>
                </a_list_three>
            </a_lag_two>
            <a_lag_two>
                <a_list_three>
                    <a_lag_three>
                        <element3>570</element3>
                        <element4>678</element4>
                    </a_lag_three>
                </a_list_three>
                <a_list_three>
                    <a_lag_three>
                        <element3>9873</element3>
                        <element4>278</element4>
                    </a_lag_three>
                </a_list_three>
                <a_list_four>
                    <a_lag_four>
                        <element5>9121</element5>
                        <element6>9879</element6>
                    </a_lag_four>
                </a_list_four>
                <a_list_three>
                    <a_lag_four>
                        <element5>098</element5>
                        <element6>231</element6>
                    </a_lag_four>
                </a_list_three>
            </a_lag_two>
        </a_list_two>
        <a_list_four>
                    <a_lag_four>
                        <element5>654</element5>
                        <element6>7665</element6>
                    </a_lag_four>
        </a_list_four>
    </a>
    <b>
        <b_list_one>
            <b_lag_one>
                <element8>123</element8>
                <element9>456</element9>
            </b_lag_one>
        </b_list_one>
    </b>
    <b>
        <b_list_one>
            <b_lag_one>
                <element8>789</element8>
                <element9>678</element9>
            </b_lag_one>            
        </b_list_one>
    </b>
</xml>

Требуемый XML:

   <xml>
    <a>
        <element0>987</element0>
        <a_list_one>
            <a_lag_one>
                <element1>123</element1>
                <element2>456</element2>
            </a_lag_one>
            <a_lag_one>
                <element1>789</element1>
                <element2>678</element2>
            </a_lag_one>
        </a_list_one>
        <a_list_two>
            <a_lag_two>
                <a_list_three>
                    <a_lag_three>
                        <element3>570</element3>
                        <element4>678</element4>
                    </a_lag_three>
                    <a_lag_three>
                        <element3>989</element3>
                        <element4>231</element4>
                    </a_lag_three>
                </a_list_three>
            </a_lag_two>
            <a_lag_two>
                <a_list_three>
                    <a_lag_three>
                        <element3>570</element3>
                        <element4>678</element4>
                    </a_lag_three>
                    <a_lag_three>
                        <element3>9873</element3>
                        <element4>278</element4>
                    </a_lag_three>
                    <a_lag_four>
                        <element5>098</element5>
                        <element6>231</element6>
                    </a_lag_four>
                </a_list_three>
                <a_list_four>
                    <a_lag_four>
                        <element5>9121</element5>
                        <element6>9879</element6>
                    </a_lag_four>
                </a_list_four>
            </a_lag_two>
        </a_list_two>
        <a_list_four>
            <a_lag_four>
                <element5>654</element5>
                <element6>7665</element6>
            </a_lag_four>
        </a_list_four>      
    </a>
    <b>
        <b_list_one>
            <b_lag_one>
                <element8>123</element8>
                <element9>456</element9>
            </b_lag_one>
            <b_lag_one>
                <element8>789</element8>
                <element9>678</element9>
            </b_lag_one>            
        </b_list_one>
    </b>
</xml>

Я ищу XSL, который выполняет преобразование вжелаемый вывод.Здесь узлы, которые имеют одно и то же имя и содержат «_LIST», должны быть объединены вместе.Однако эта логика должна происходить только внутри первого узла "_LIST" и не должна применяться к внутренним узлам.Во-вторых, на корневом уровне также объединяемые узлы.Например, здесь должен быть только один тег "a" и тег "b".Пожалуйста, помогите.

Ответы [ 2 ]

0 голосов
/ 12 октября 2018

Вот решение для XSLT 1.0

  <xsl:stylesheet version="1.0"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes"/>

    <xsl:key name="xmlChildren" match="xml/*" use="local-name()"/>
    <xsl:key name="list" match="*[contains(local-name(),'_list')]" use="generate-id(..)"/>

    <!-- Select the child nodes of the xml node. -->
    <xsl:template match="xml/*">
      <!-- Get the name of the current node. -->
      <xsl:variable name="localName" select="local-name()"/>
      <!-- Is this the first child of the xml node with this name? -->
      <xsl:if test="generate-id(.) = generate-id(key('xmlChildren', $localName)[1])">
        <xsl:copy>
          <!-- Output all of the xml grandchild nodes of any xml child node with same name as the current node. -->
          <xsl:apply-templates select="key('xmlChildren', $localName)/*">
              <xsl:with-param name="parentName" select="$localName"/>
          </xsl:apply-templates>
        </xsl:copy>
      </xsl:if>
    </xsl:template>

    <!-- Select the nodes with a local name that contains '_list'. -->
    <xsl:template match="*[contains(local-name(),'_list')]">
      <xsl:param name="parentName"/>

      <xsl:variable name="parentID" select="generate-id(..)"/>

      <!-- Get the name of the current node. -->
      <xsl:variable name="localName" select="local-name()"/>

      <xsl:choose>
        <!-- Is this list a first generation grandchild of xml? -->
        <xsl:when test="parent::*/parent::xml">
          <!-- Is this the first instance of this list? -->
          <xsl:if test="generate-id(.) = generate-id(key('xmlChildren', $parentName)/*[local-name()=$localName][1])">
            <xsl:copy>
              <xsl:apply-templates select="key('xmlChildren', $parentName)/*[local-name()=$localName]/*"/>
            </xsl:copy>
          </xsl:if> 
        </xsl:when>
        <xsl:otherwise>
          <!-- Is this the first instance of this list? -->
          <xsl:if test="generate-id(.) = generate-id(key('list', $parentID)[local-name()=$localName][1])">
            <xsl:copy>
              <xsl:apply-templates select="key('list', $parentID)[local-name() = $localName]/*"/>
            </xsl:copy>
          </xsl:if>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

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

  </xsl:stylesheet>
0 голосов
/ 12 октября 2018

Я думаю, что в XQuery 3 вы можете решить эту проблему, используя два вложенных выражения for .. group by:

/*/element { node-name(.) } {
    for $child-element at $pos in *
    group by $element-name := node-name($child-element)
    order by $pos[1]
    return
        element { $element-name } {
            for $grand-child at $pos in $child-element/*
            let $grand-child-name := node-name($grand-child)
            group by $key := $grand-child-name, $handle := contains(string($grand-child-name), '_list')
            order by $pos[1]
            return
                if ($handle)
                then
                    element { $key } {
                        $grand-child/*
                    }
                else $grand-child
        }
}

https://xqueryfiddle.liberty -development.net / pPgCcor

Для XSLT 1 я хотел бы использовать ключи, как уже предложенное решение, но я думаю, что тогда проще использовать два разных шаблона соответствия для каждого ключа, по одному для первого элемента в группе, созданной ключом, который создает копию и обрабатывает дочерние узлыгруппа, а вторая пуста, чтобы подавить обработку дублированных имен элементов группы:

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="child-group" match="/*/*" use="name()"/>
  <xsl:key name="grand-child-group" match="/*/*/*[contains(local-name(), '_list')]" use="name()"/>

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

  <xsl:template match="/*/*[generate-id() = generate-id(key('child-group', name())[1])]">
      <xsl:copy>
          <xsl:apply-templates select="key('child-group', name())/node()"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="/*/*[not(generate-id() = generate-id(key('child-group', name())[1]))]"/>

  <xsl:template match="/*/*/*[contains(local-name(), '_list')][generate-id() = generate-id(key('grand-child-group', name())[1])]">
      <xsl:copy>
          <xsl:apply-templates select="key('grand-child-group', name())/node()"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="/*/*/*[contains(local-name(), '_list')][not(generate-id() = generate-id(key('grand-child-group', name())[1]))]"/>  

</xsl:stylesheet>

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

На основеВ вашем комментарии я также попытался сделать рекурсивное решение XQuery 3:

declare function local:group($elements as element()*) as element()*
{
  for $child-element at $pos in $elements
  let $child-name := node-name($child-element)
  group by $name-group := $child-name, $match := contains(string($child-name), '_list')
  order by $pos[1]
  return
      if ($match)
      then element { $name-group } {
          local:group($child-element/*)
      }
      else if (not($child-element/*))
      then $child-element
      else $child-element/element {$name-group} { local:group(*) }
};

/*/element { node-name(.) } {
    for $child-element at $pos in *
    group by $element-name := node-name($child-element)
    order by $pos[1]
    return element { $element-name } {
         local:group($child-element/*)
    }

}

https://xqueryfiddle.liberty -development.net / pPgCcor / 1

...