XSLT embedding (positional grouping key?) problem. newbie

Discussion in 'XML' started by I.M. Postor, Sep 26, 2006.

  1. I.M. Postor

    I.M. Postor Guest

    Hello,

    Maybe this is a case of positional grouping, but my level of xslt has me
    stuck here.

    I have some XML where some elements haven't the proper name (level2
    instead of level3) and aren't properly embedded. A level2 element should
    always be embedded in a level1 element. a level3 element should always
    be embedded in a level2 element. An element <levelX type="single">
    cannot embed other levelX elements. The amount of elements <levelX
    type="single"> _to_be_embedded_ depends on the value of the attribute
    "embed" above.

    Input:

    <?xml version="1.0" ?>
    <root>
    <level1 type="other"><name>A</name>
    <level2 type="group" embed="4"><name>B</name>
    <level3 type="single"><name>C</name>
    </level3>
    </level2> <!-- level2 type="group" is closed here; too soon-->

    <level2 type="single"><name>D</name>
    </level2>
    <level2 type="single"><name>E</name>
    </level2>
    <level2 type="single"><name>F</name>
    </level2>
    <!-- level2 type="group" closing tag should be here -->
    <level2 type="single"><name>G</name>
    </level2>
    <level2 type="single"><name>H</name>
    </level2>
    <level2 type="single"><name>I</name>
    </level2>
    <!-- ...-->
    </level1>
    </root>




    So in this example the element with name C is already properly embedded,
    but not D, E, F.


    I have this stylesheet:

    <?xml version="1.0" encoding="iso-8859-1"?>
    <xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" >
    <xsl:eek:utput method="xml" indent="yes" />
    <xsl:template match="*/comment()" />


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


    <xsl:template match="/root/level1/level2[@type='group']">
    <xsl:variable name="volumes" select="@embed" as="xs:integer" />
    <xsl:variable name="already_embedded"
    select="count(child::level3)" as="xs:integer" />
    <xsl:variable name="to_embed"
    select="$volumes - $already_embedded" as="xs:integer" />

    <xsl:element name="level2">
    <xsl:copy-of select="@*" />
    <xsl:copy-of select="node()" />

    <xsl:for-each
    select="following-sibling::level2[position() &lt;= $to_embed]">
    <xsl:element name="level3X"> <!-- X just as a marker -->
    <xsl:copy-of select="@*" />
    <xsl:copy-of select="node()" />
    </xsl:element>
    </xsl:for-each>

    </xsl:element>
    </xsl:template>

    </xsl:stylesheet>



    which produces:

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
    <level1 type="other">
    <name>A</name>
    <level2 type="group" embed="4">
    <name>B</name>
    <level3 type="single">
    <name>C</name>
    </level3>
    <level3X type="single">
    <name>D</name>
    </level3X>
    <level3X type="single">
    <name>E</name>
    </level3X>
    <level3X type="single">
    <name>F</name>
    </level3X>
    </level2>

    <level2 type="single">
    <name>D</name>
    </level2>
    <level2 type="single">
    <name>E</name>
    </level2>
    <level2 type="single">
    <name>F</name>
    </level2>

    <level2 type="single">
    <name>G</name>
    </level2>
    <level2 type="single">
    <name>H</name>
    </level2>
    <level2 type="single">
    <name>I</name>
    </level2>

    </level1>
    </root>



    where of course D-F are doubled, which is not what I want.
    I think I might have to do something clever with keys and generate-id() like:

    <xsl:template match="/root/level1/level2[@type='single']">
    <xsl:if generate-id(.) != generate-id(key('???', ???))
    <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
    </xsl:if>
    </xsl:template>


    But, given that this is the right approach, I have no idea how to declare a key
    in the toplevel (directly under xsl:stylesheet), give it a value under the
    for-each loop _and_ get to that value in the level2[@type='single'] template.

    Any help is appreciated,


    Cheers
     
    I.M. Postor, Sep 26, 2006
    #1
    1. Advertising

  2. I.M. Postor

    Guest

    I.M. Postor wrote:
    > I have some XML where some elements haven't the proper
    > name (level2 instead of level3) and aren't properly
    > embedded. A level2 element should always be embedded in a
    > level1 element. a level3 element should always be
    > embedded in a level2 element. An element <levelX
    > type="single"> cannot embed other levelX elements. The
    > amount of elements <levelX type="single">
    > _to_be_embedded_ depends on the value of the attribute
    >"embed" above.
    >
    > <?xml version="1.0"?>
    > <root>
    > <level1 type="other"><name>A</name>
    > <level2 type="group" embed="4"><name>B</name>
    > <level3 type="single"><name>C</name>
    > </level3>
    > </level2>
    > <!-- level2 type="group" is closed here; too soon-->
    > <level2 type="single"><name>D</name>
    > </level2>
    > <level2 type="single"><name>E</name>
    > </level2>
    > <level2 type="single"><name>F</name>
    > </level2>
    > <!-- level2 type="group" closing tag should be here -->
    > <level2 type="single"><name>G</name>
    > </level2>
    > <level2 type="single"><name>H</name>
    > </level2>
    > <level2 type="single"><name>I</name>
    > </level2>
    > <!-- ...-->
    > </level1>
    > </root>
    >
    > So in this example the element with name C is already
    > properly embedded, but not D, E, F.
    >
    > I have this stylesheet:


    [XSLT]

    > which produces:


    [result with copied instead of moved nodes]

    > where of course D-F are doubled, which is not what I
    > want.


    The problem, of course, is that you're using the identity
    transformation, but fail to exclude the nodes you're trying
    to move.

    The following works on your sample XML, although you might
    need to tweak it if you intend to use it:

    <?xml version="1.0" encoding="iso-8859-1"?>
    <xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:eek:utput
    method="xml"
    version="1.0"
    encoding="iso-8859-1"/>
    <xsl:template match="*/comment()"/>
    <xsl:template match="@*|node()">
    <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
    </xsl:template>
    <xsl:template
    match="*[@type = 'group']">
    <xsl:variable name="volumes" select="@embed"/>
    <xsl:variable
    name="already_embedded"
    select="count(child::*[@type = 'single'])"/>
    <xsl:variable
    name="to_embed"
    select="$volumes - $already_embedded"/>
    <xsl:element name="{name()}">
    <xsl:copy-of select="@*"/>
    <xsl:copy-of select="node()"/>
    <xsl:apply-templates
    select="
    following-sibling::*[@type = 'single']
    [
    position() &lt;= $to_embed
    ]"
    mode="move"/>
    </xsl:element>
    </xsl:template>
    <!-- exclude the nodes that we've moved -->
    <xsl:template
    match="
    *
    [
    (
    preceding-sibling::*[@type = 'group'][1]/@embed
    )&gt;
    count
    (
    preceding-sibling::*[@type = 'group'][1]/
    *[@type = 'single']|
    preceding-sibling::*[@type = 'single']
    [
    generate-id
    (
    preceding-sibling::*[@type='group'][1]
    ) =
    generate-id
    (
    current()/
    preceding-sibling::*[@type='group'][1]
    )
    ]
    )
    ]"/>
    <xsl:template match="level2" mode="move">
    <xsl:element name="level3">
    <xsl:attribute name="moved">yes</xsl:attribute>
    <xsl:copy-of select="@*"/>
    <xsl:copy-of select="node()"/>
    </xsl:element>
    </xsl:template>
    </xsl:stylesheet>

    --
    Pavel Lepin
     
    , Sep 26, 2006
    #2
    1. Advertising

  3. I.M. Postor

    I.M. Postor Guest

    Hello Pavel

    On 26 Sep 2006 02:36:23 -0700, wrote:

    > The following works on your sample XML, although you might
    > need to tweak it if you intend to use it:


    'Off the shelf', I think is the expression: worked immediately. Thanks
    much. I'm still studying on the exclusion template though.


    Cheers
     
    I.M. Postor, Sep 27, 2006
    #3
    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. Tomi Silander
    Replies:
    3
    Views:
    979
    Grant Edwards
    Apr 6, 2005
  2. Replies:
    0
    Views:
    363
  3. Andy

    Positional Grouping

    Andy, Jan 10, 2007, in forum: XML
    Replies:
    0
    Views:
    469
  4. MisterWilliam
    Replies:
    1
    Views:
    338
    George Sakkis
    Jun 18, 2008
  5. Peter Otten
    Replies:
    1
    Views:
    496
    rantingrick
    Feb 1, 2011
Loading...

Share This Page