Deepening an XML hierarchy

Discussion in 'XML' started by Juho Jussila, Jul 14, 2005.

  1. Juho Jussila

    Juho Jussila Guest

    Hi

    I have a problem while trying to deepen a flat XML document.
    The problem is that there are no links between elements. The only
    way to determine which element should be a child of another
    is to check its position.

    Input document is:
    <?xml version="1.0" encoding="iso-8859-1"?>
    <Root>
    <A1/>
    <B1/>
    <B1/>
    <C1/>
    <C1/>
    <D1/>
    <B2/>
    <C2/>
    <Root>

    Element can have 0-n sub-elements:
    A1: B1,B2
    B1: C1
    C1: D1
    B2: C2

    And I'd like to have this kind of result document:
    <A1>
    <B1/>
    <B1>
    <C1/>
    <C1>
    <D1/>
    </C1>
    </B1>
    <B2>
    <C2/>
    </B2>
    </A1>

    After reading FAQ
    (http://www.dpawson.co.uk/xsl/sect2/N4486.html#d252e1102)
    I managed to write this:

    <?xml version="1.0" encoding="iso-8859-1"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/Root">
    <xsl:apply-templates select="A1"/>
    </xsl:template

    <xsl:template match="A1">
    <xsl:copy>
    <xsl:copy-of select="*"/>
    <xsl:apply-templates
    select="following-sibling::*[1][name()='B1' or name()='B2']" />
    </xsl:copy>
    </xsl:template>

    <xsl:template match="B1|B2">
    <xsl:copy-of select="."/>
    <xsl:apply-templates
    select="following-sibling::*[1][name()='B1' or name()='B2']"/>
    </xsl:template>

    </xsl:stylesheet>

    But now I don't have any idea how to travel next levels.
    If I add apply-templates select="C1" inside the last template,
    "iterator" position is lost. For example in sequence A1 B1 C1 B1 C1
    the last B1 is not matched.

    Is there any way to do this with xslt?

    Thanks in advance.

    --
    //
    \X/ http://www.iki.fi/~jussila
    Juho Jussila, Jul 14, 2005
    #1
    1. Advertising

  2. $ saxon flat1.xml flat1.xsl
    <?xml version="1.0" encoding="utf-8"?>
    <root>
    <A1>
    <B1/>
    <B1>
    <C1/>
    <C1>
    <D1/>
    <B2>
    <C2/>
    </B2>
    </C1>
    </B1>
    </A1>
    </root>



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

    <xsl:eek:utput indent="yes"/>
    <xsl:variable name="s" select="'ABCDEFGHIJK'"/>

    <xsl:template match="Root">
    <root>
    <xsl:apply-templates select="*[1]"/>
    </root>
    </xsl:template>

    <xsl:template match="*">
    <xsl:variable name="f" select="substring(name(),1,1)"/>
    <xsl:variable name="c" select="string-length(substring-before($s,$f))"/>
    <xsl:variable name="n" select="following-sibling::*[1]"/>
    <xsl:variable name="nf" select="substring(name($n),1,1)"/>
    <xsl:variable name="nc" select="string-length(substring-before($s,$nf))"/>
    <xsl:copy>
    <xsl:apply-templates select="$n[$nc &gt;$c]"/>
    </xsl:copy>
    <xsl:apply-templates select="$n[$nc &lt;=$c]"/>
    </xsl:template>

    </xsl:stylesheet>
    David Carlisle, Jul 14, 2005
    #2
    1. Advertising

  3. Juho Jussila

    Juho Jussila Guest

    David Carlisle <> writes:

    > <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    > version="1.0">
    >
    > <xsl:eek:utput indent="yes"/>
    > <xsl:variable name="s" select="'ABCDEFGHIJK'"/>
    >
    > <xsl:template match="Root">
    > <root>
    > <xsl:apply-templates select="*[1]"/>
    > </root>
    > </xsl:template>
    >
    > <xsl:template match="*">
    > <xsl:variable name="f" select="substring(name(),1,1)"/>
    > <xsl:variable name="c" select="string-length(substring-before($s,$f))"/>
    > <xsl:variable name="n" select="following-sibling::*[1]"/>
    > <xsl:variable name="nf" select="substring(name($n),1,1)"/>
    > <xsl:variable name="nc" select="string-length(substring-before($s,$nf))"/>
    > <xsl:copy>
    > <xsl:apply-templates select="$n[$nc &gt;$c]"/>
    > </xsl:copy>
    > <xsl:apply-templates select="$n[$nc &lt;=$c]"/>
    > </xsl:template>
    >
    > </xsl:stylesheet>


    Thanks for your quick reply. XSLT really is magic :)

    Too bad, I wrote too simplified example and mislead you. Sorry.
    Actual input is (well, only part of it):

    <?xml version="1.0" encoding="iso-8859-1"?>
    <Root>
    <H01/>
    <H04/>
    <H02/>
    <H03/>
    <H05/>
    <H02/>
    <H02/>
    <I01/>
    <I02/>
    <I02/>
    <I05/>
    <I05/>
    <Root>

    Structure is documented in textual form:
    H01
    H04
    H02
    H05
    H03
    I01
    I02
    I05
    VJU09
    VJU02
    VJU12
    VJU02
    ....

    So simple element name doesn't tell where it should be placed.
    Is there still hope for XSLT based solution?

    --
    //
    \X/ http://www.iki.fi/~jussila
    Juho Jussila, Jul 14, 2005
    #3

  4. > Too bad, I wrote too simplified example and mislead you. Sorry.


    well I suspected that your real case didn't just use the first letter,
    but...

    > Structure is documented in textual form:

    I don't understand this.

    > Is there still hope for XSLT based solution?

    If there is some procedure that says when an element should be nested
    and when it should not, then xslt can encode that.
    However my understanding of your description is that you just give a
    complete description of the final answer. Clearly if you know what the
    answer has to be, XSLT can just copy the required answer and ignore its
    input, but I suspect that isn't quite what you want either:)

    although looking again, perhaps I can make one guess at what you mean...



    flat2.xml
    <Root>
    <H01/>
    <H04/>
    <H02/>
    <H03/>
    <H05/>
    <H02/>
    <H02/>
    <I01/>
    <I02/>
    <I02/>
    <I05/>
    <I05/>
    </Root>

    flat2a.xml
    <x>
    <x name="H01">
    <x name="H04"/>
    <x name="H02">
    <x name="H05"/>
    <x name="H03"/>
    </x>
    </x>
    <x name="I01">
    <x name="I02">
    <x name="I05"/>
    </x>
    </x>
    <x name="VJU09">
    <x name="VJU02"/>
    <x name="VJU12">
    <x name="VJU02"/>
    </x>
    </x>
    </x>


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

    <xsl:eek:utput indent="yes"/>
    <xsl:variable name="s" select="document('flat2a.xml')"/>
    <xsl:template match="Root">
    <root>
    <xsl:apply-templates select="*[1]"/>
    </root>
    </xsl:template>

    <xsl:template match="*">
    <xsl:variable name="t" select="name()"/>
    <xsl:variable name="n" select="$s//*[@name=$t]/../*/@name"/>
    <xsl:variable name="c" select="$s//*[@name=$t]/*/@name"/>
    <xsl:copy>
    <xsl:apply-templates select="following-sibling::*[1][name()=$c]"/>
    </xsl:copy>
    <xsl:apply-templates select="following-sibling::*[name()=$n][1]"/>
    </xsl:template>

    </xsl:stylesheet>


    $ saxon flat2.xml flat2.xsl
    <?xml version="1.0" encoding="utf-8"?>
    <root>
    <H01>
    <H04/>
    <H02>
    <H03/>
    <H05/>
    </H02>
    <H02/>
    <H02/>
    </H01>
    <I01>
    <I02/>
    <I02>
    <I05/>
    <I05/>
    </I02>
    </I01>
    </root>
    David Carlisle, Jul 14, 2005
    #4
  5. Juho Jussila

    Mukul Gandhi Guest

    I am not sure whether this will help, but here is my attempt..

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

    <xsl:eek:utput method="xml" indent="yes" />

    <xsl:template match="/Root">
    <xsl:call-template name="generateHierarchy">
    <xsl:with-param name="x" select="*[1]" />
    <xsl:with-param name="nodeset" select="*[position() &gt; 1]" />

    </xsl:call-template>
    </xsl:template>

    <xsl:template name="generateHierarchy">
    <xsl:param name="x" />
    <xsl:param name="nodeset" />

    <xsl:choose>
    <xsl:when test="(name($x) = name($nodeset[1])) and (name($x) !=
    '') and (name($nodeset[1]) != '')">
    <xsl:element name="{name($x)}" />
    <xsl:element name="{name($nodeset[1])}">
    <xsl:call-template name="generateHierarchy">
    <xsl:with-param name="x" select="$nodeset[1]" />
    <xsl:with-param name="nodeset" select="$nodeset[position()
    &gt; 1]" />
    </xsl:call-template>
    </xsl:element>
    </xsl:when>
    <xsl:eek:therwise>
    <xsl:if test="name($x) != ''">
    <xsl:element name="{name($x)}">
    <xsl:call-template name="generateHierarchy">
    <xsl:with-param name="x" select="$nodeset[1]" />
    <xsl:with-param name="nodeset" select="$nodeset[position() &gt;
    1]" />
    </xsl:call-template>
    </xsl:element>
    </xsl:if>
    </xsl:eek:therwise>
    </xsl:choose>

    </xsl:template>

    </xsl:stylesheet>

    I get output -
    <?xml version="1.0" encoding="UTF-8"?>
    <H01>
    <H04>
    <H02>
    <H03>
    <H05>
    <H02/>
    <H02>
    <H02>
    <I01>
    <I02/>
    <I02>
    <I02>
    <I05/>
    <I05>
    <I05/>
    </I05>
    </I02>
    </I02>
    </I01>
    </H02>
    </H02>
    </H05>
    </H03>
    </H02>
    </H04>
    </H01>

    I am using Saxon 8.4.

    Regards,
    Mukul
    Mukul Gandhi, Jul 14, 2005
    #5
  6. Juho Jussila

    Juho Jussila Guest

    David Carlisle <> writes:

    > > Structure is documented in textual form:

    > I don't understand this.


    Sorry, my English is quite poor.. I was trying to say that the XML
    hierarchy is documented in Word document.

    [...]

    > <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    > version="1.0">
    >
    > <xsl:eek:utput indent="yes"/>
    > <xsl:variable name="s" select="document('flat2a.xml')"/>
    > <xsl:template match="Root">
    > <root>
    > <xsl:apply-templates select="*[1]"/>
    > </root>
    > </xsl:template>
    >
    > <xsl:template match="*">
    > <xsl:variable name="t" select="name()"/>
    > <xsl:variable name="n" select="$s//*[@name=$t]/../*/@name"/>
    > <xsl:variable name="c" select="$s//*[@name=$t]/*/@name"/>
    > <xsl:copy>
    > <xsl:apply-templates select="following-sibling::*[1][name()=$c]"/>
    > </xsl:copy>
    > <xsl:apply-templates select="following-sibling::*[name()=$n][1]"/>
    > </xsl:template>
    >
    > </xsl:stylesheet>


    Wow, this is exactly what I need and IMHO _very_ elegant solution. The
    idea of putting XML structure in separate file make this generic.

    Thank you very much, I really appreciate your help.

    --
    //
    \X/ http://www.iki.fi/~jussila
    Juho Jussila, Jul 15, 2005
    #6
  7. Juho Jussila

    Juho Jussila Guest

    "Mukul Gandhi" <> writes:

    > I am not sure whether this will help, but here is my attempt..


    Thanks, nice solution also. Maybe I express my intention unclearly,
    but your XSLT gives a bit too deep hierarchy.

    Hierarchy is specified:
    H01 may have child elements: H04, H02
    H02 may have child elements: H05, H03
    I01 may have child elements: I02
    I02 may have child elements: I05
    and so on..

    It would be easy to flatten any kind of XML hierarchy performing a
    preorder traversal. Problem is that the input document is flat and I
    need to do the other way.

    [...]

    > <H01>
    > <H04>
    > <H02>
    > <H03>
    > <H05>
    > <H02/>
    > <H02>
    > <H02>
    > <I01>
    > <I02/>
    > <I02>
    > <I02>
    > <I05/>
    > <I05>
    > <I05/>
    > </I05>
    > </I02>
    > </I02>
    > </I01>
    > </H02>
    > </H02>
    > </H05>
    > </H03>
    > </H02>
    > </H04>
    > </H01>



    --
    //
    \X/ http://www.iki.fi/~jussila
    Juho Jussila, Jul 15, 2005
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Alan Pretre

    Re: hierarchy chart

    Alan Pretre, Jul 25, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    1,261
    Alan Pretre
    Jul 25, 2003
  2. Replies:
    7
    Views:
    611
    Joris Gillis
    Dec 10, 2004
  3. csharp

    Sorting XML into a Hierarchy

    csharp, Aug 4, 2006, in forum: XML
    Replies:
    0
    Views:
    425
    csharp
    Aug 4, 2006
  4. Bostonasian
    Replies:
    8
    Views:
    485
    Peter Flynn
    Feb 15, 2007
  5. Erik Wasser
    Replies:
    5
    Views:
    437
    Peter J. Holzer
    Mar 5, 2006
Loading...

Share This Page