0

I want to create an XML with a hierarchic structure from a flat XML file. The XML file contains the parent-child relations and if a node is a leaf or not.

There is no explicit information about the level of nesting or the root nodes in the structure. The XML-file can contain more than one root element and it is not ordered.

<list>
<stru><parent>A01</parent><child>P04</child></stru>
<stru><parent>B11</parent><child>P01</child></stru>
<stru><parent>B12</parent><child>P01</child></stru>
<stru><parent>B12</parent><child>P03</child></stru>
<stru><parent>B21</parent><child>P02</child></stru>
<stru><parent>B21</parent><child>P03</child></stru>
<item><cod>B01</cod><isparent>Y</isparent></item>
<item><cod>B11</cod><isparent>Y</isparent></item>
<item><cod>B12</cod><isparent>Y</isparent></item>
<item><cod>B21</cod><isparent>Y</isparent></item>
<item><cod>P01</cod><isparent>N</isparent></item>
<item><cod>P02</cod><isparent>N</isparent></item>
<item><cod>P03</cod><isparent>N</isparent></item>
<item><cod>A01</cod><isparent>Y</isparent></item>
<item><cod>P04</cod><isparent>N</isparent></item>
<stru><parent>B01</parent><child>B11</child></stru>
<stru><parent>B01</parent><child>B12</child></stru>
<stru><parent>B11</parent><child>B21</child></stru>
</list>

The needed result is shown below. I don't know how to do this.

I found a transformation that creates that structure if the root node is known.

<list>
    <itemlist>
        <item>
            <cod>A01</cod>
            <itemlist>
                <item>
                    <cod>P04</cod>
                </item>
            </itemlist>
        </item>
        <item>              
            <cod>B01</cod>
            <itemlist>
                <item>
                    <cod>B11</cod>
                    <itemlist>
                        <item>
                            <cod>B21</cod>
                            <itemlist>
                                <item>
                                    <cod>P02</cod>
                                </item>
                                <item>
                                    <cod>P03</cod>
                                </item>
                            </itemlist> 
                        </item>
                        <item>
                            <cod>P01</cod>
                        </item>
                    </itemlist>
                </item>
                <item>
                    <cod>B12</cod>
                    <itemlist>
                        <item>
                            <cod>P01</cod>
                        </item> 
                        <item>
                            <cod>P02</cod>
                        </item> 
                    </itemlist> 
                </item>
            </itemlist>
        </item>
    </itemlist>
</list>
2
  • "The XML-file can contain more than one root element" No. An XML file must contain exactly one root element. Otherwise it's not an XML file. Commented Jul 31, 2014 at 15:09
  • Or did you mean that the tree described by the XML file can have more than one root item? Commented Jul 31, 2014 at 16:33

1 Answer 1

1

The following stylesheet:

XSLT 1.0

<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="link-by-child" match="stru" use="child" />
<xsl:key name="link-by-parent" match="stru" use="parent" />
<xsl:key name="item-by-code" match="item" use="cod" />

<xsl:template match="/">
    <list>
        <itemlist>
            <!-- select progenitors (items that are not children of any other item) -->
            <xsl:apply-templates select="list/item[not(key('link-by-child', cod))]"/>
        </itemlist>
    </list>
</xsl:template>

<xsl:template match="item[isparent='Y']">
    <xsl:copy>
        <xsl:copy-of select="cod"/>
        <itemlist>
            <!-- select item's children -->
            <xsl:apply-templates select="key('item-by-code', key('link-by-parent', cod)/child)"/>
        </itemlist></xsl:copy>
</xsl:template>

<xsl:template match="item[isparent='N']">
    <xsl:copy>
        <xsl:copy-of select="cod"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

when applied to your example input, will return:

<?xml version="1.0" encoding="UTF-8"?>
<list>
   <itemlist>
      <item>
         <cod>B01</cod>
         <itemlist>
            <item>
               <cod>B11</cod>
               <itemlist>
                  <item>
                     <cod>B21</cod>
                     <itemlist>
                        <item>
                           <cod>P02</cod>
                        </item>
                        <item>
                           <cod>P03</cod>
                        </item>
                     </itemlist>
                  </item>
                  <item>
                     <cod>P01</cod>
                  </item>
               </itemlist>
            </item>
            <item>
               <cod>B12</cod>
               <itemlist>
                  <item>
                     <cod>P01</cod>
                  </item>
                  <item>
                     <cod>P03</cod>
                  </item>
               </itemlist>
            </item>
         </itemlist>
      </item>
      <item>
         <cod>A01</cod>
         <itemlist>
            <item>
               <cod>P04</cod>
            </item>
         </itemlist>
      </item>
   </itemlist>
</list>

which I believe is identical to your expected output, except for the ordering of the branches.

Sign up to request clarification or add additional context in comments.

1 Comment

Amazing and very elegant. Thank you very much indeed!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.