XSLT Create a hierarchical structure from a flat structure by using certain level criteria

自闭症网瘾萝莉.ら 提交于 2019-12-02 11:46:33

Here is a suggestion using a recursive function:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="mf">

  <xsl:output indent="yes"/>

  <xsl:function name="mf:group" as="element()*">
      <xsl:param name="nodes" as="element(node)*"/>
      <xsl:param name="level" as="xs:integer"/>
      <xsl:for-each-group select="$nodes" group-starting-with="node[metaInfo/Level = $level and @groupMaxOccurs != '']">
        <xs:element name="{@nodeTag}" minOccurs="{@minOccurs}" maxOccurs="{@groupMaxOccurs}" id="{metaInfo/ID}">
            <xs:element name="{@nodeTag}" minOccurs="0" maxOccurs="1" id="{metaInfo/ID}"/>
            <xsl:choose>
                <xsl:when test="(current-group() except .)/metaInfo/Level = $level + 1">
                    <xsl:sequence select="mf:group(current-group() except ., $level + 1)"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="current-group() except ."/>
                </xsl:otherwise>
            </xsl:choose>
        </xs:element>
      </xsl:for-each-group>
  </xsl:function>

  <xsl:template match="nodes">
      <xs:schema>
          <xsl:sequence select="mf:group(node, 1)"/>
      </xs:schema>
  </xsl:template>

  <xsl:template match="node">
      <xs:element name="{@nodeTag}" minOccurs="0" maxOccurs="{@segmentMaxOccurs}" id="{metaInfo/ID}"/>
  </xsl:template>

</xsl:transform>

You will have to test yourself with more levels or provide some input samples with deeper nesting to allow us to test.

Let me suggest a different approach to creating the nested hierarchy. To demonstrate, I will use the following minimized input:

XML

<nodes>
   <node nodeTag="A">
      <metaInfo>
         <Level>1</Level>
      </metaInfo>
   </node>
   <node nodeTag="Ab">
      <metaInfo>
         <Level>2</Level>
      </metaInfo>
   </node>
   <node nodeTag="Ab1">
      <metaInfo>
         <Level>3</Level>
      </metaInfo>
   </node>
   <node nodeTag="Ab2">
      <metaInfo>
         <Level>3</Level>
      </metaInfo>
   </node>
   <node nodeTag="Ac">
      <metaInfo>
         <Level>2</Level>
      </metaInfo>
   </node>
   <node nodeTag="B">
      <metaInfo>
         <Level>1</Level>
      </metaInfo>
   </node>
   <node nodeTag="C">
      <metaInfo>
         <Level>1</Level>
      </metaInfo>
   </node>
   <node nodeTag="D">
      <metaInfo>
         <Level>1</Level>
      </metaInfo>
   </node>
   <node nodeTag="Da">
      <metaInfo>
         <Level>2</Level>
      </metaInfo>
   </node>
   <node nodeTag="Db">
      <metaInfo>
         <Level>2</Level>
      </metaInfo>
   </node>
</nodes>

Applying the following stylesheet:

XSLT

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

<xsl:key name="child-by-parent" 
         match="node" 
         use="generate-id(preceding-sibling::node[metaInfo/Level=current()/metaInfo/Level - 1][1])" />

<xsl:template match="nodes">
    <root>
        <xsl:apply-templates select="node[metaInfo/Level=1]"/>
    </root>
</xsl:template>

<xsl:template match="node">
    <element name="{@nodeTag}">
        <xsl:apply-templates select="key('child-by-parent', generate-id())"/>
    </element>
</xsl:template>

</xsl:stylesheet>

will return:

Result

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <element name="A">
      <element name="Ab">
         <element name="Ab1"/>
         <element name="Ab2"/>
      </element>
      <element name="Ac"/>
   </element>
   <element name="B"/>
   <element name="C"/>
   <element name="D">
      <element name="Da"/>
      <element name="Db"/>
   </element>
</root>

This works recursively with any number of levels.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!