tags:

views:

80

answers:

1

Hi!

I have two problems with the below code that I need help fixing:

1) It doesn't include the root node when returning results from the XPath expression. (I've tried a couple things, but it messes up the results even more..)

2) I need help fixing the formatting of the results. I need the same nodes with the same attributes to be listed under one heading instead of having a heading for every result. I had a similar question earlier, but now that issues with the code were fixed, I can't seem to get my headings to work properly without compromising the results.

Besides the issue with missing the root node, I believe this returns the correct results, therefore I do not want to change the code drastically.

Here is some intentionally inconsistent dummy XML I am testing with:

<pets name="myPets" NUM="2">
    <dog name="allMyDogs" NUM="5">
        <dog name="Frank" cute="yes" color"brown" type="Lab" NUM="3"/>
        <dog name="Frank" NUM="3"/>
        <dog name="Spot"  NUM="4"/>
        <dog name="Rover" cute="yes" NUM="1"/>
        <dog name="Rupert" cute="yes" type="Pug" color="black" NUM="6"/>
        <cat name="Lucy" cute="yes" NUM="4"/>
    </dog>
    <cat name="allMyCats" NUM="4">
        <cat name="Simba" cute="yes" NUM="4"/>
        <cat name="Princess" cute="no" color="black" NUM="5"/>
        <cat name="Fluffy" cute="yes" color="grey" NUM="1"/>
        <cat name="Lucy" cute="yes" color="brown" NUM="3">
            <cat name="Lucy" cute="no"  NUM="35"/>
            <cat name="Lucy" cute="yes" purrs="yes" NUM="6"/>
        </cat>
        <cat name="Lucy"cute="no" color="grey" NUM="1"/>
        <dog name="Rover" cute="yes" NUM="24"/>
    </cat>
    <cat name="Lucy" NUM="9"/>
    <dog name="Rupert Jr" cute="yes" type="Pug" color="black" NUM="0"/>
</pets>

And here is the XSLT code:

    <xsl:key name="elem_key" match="elem" use="concat(@key, .)" />

    <xsl:variable name="all_data">
        <xsl:apply-templates select="*//*">
            <xsl:sort select="name()" />
        </xsl:apply-templates>
    </xsl:variable>

    <xsl:template match="//*[@NUM&lt;=4]">
        <elem key="{name()}">
            <xsl:copy-of select="@*" />
            <xsl:for-each select="@*">
                <xsl:sort select="name()" />
                <attribute>|<xsl:value-of select="name()" />|</attribute>
            </xsl:for-each>
        </elem>
    </xsl:template>

    <xsl:template match="/">
        <html>
            <body>
                <xsl:for-each select="msxsl:node-set($all_data)">
                    <xsl:for-each select="*[generate-id()=generate-id(key('elem_key',concat(@key, .))[1])]">
                        <table >
                            <tr>
                                <td>Element Name</td>
                                <xsl:for-each select="*">
                                    <td>
                                        <xsl:value-of select="translate(.,'|','')" />
                                    </td>
                                </xsl:for-each>
                            </tr>
                            <xsl:for-each select="key('elem_key', concat(@key, .))">
                                <xsl:variable name="curr_elem" select="." />
                                <tr>
                                    <td>
                                        <xsl:value-of select="@key" />
                                    </td>
                                    <xsl:for-each select="*">
                                        <td >
                                            <xsl:value-of select="$curr_elem/@*[name()=translate(current(),'|','')]" />
                                        </td>
                                    </xsl:for-each>
                                </tr>
                            </xsl:for-each>
                        </table>
                        <p />
                    </xsl:for-each>
                </xsl:for-each>
            </body>
        </html>
    </xsl:template>

And the desired formatting for output:

Element Name  Name    NUM           
pets          myPets  2         

Element Name  Name       Cute  Color  NUM  Type
dog           Frank      yes   brown  3    Lab
dog           Rupert Jr  yes   black  0    Pug

Element Name  Name   NUM            
dog           Frank  3          
dog           Spot   4          

Element Name  Name   Cute  NUM      
dog           Rover  yes   1        

Element Name  Name   Cute  NUM      
cat           Lucy   yes   4        
cat           Simba  yes   4        

Element Name  Name       NUM            
cat           allMyCats  4          

Element Name  Name    Color  Cute  NUM  
cat           Fluffy  grey   yes   1    
cat           Lucy    brown  yes   3    
cat           Lucy    grey   no    1

Thanks! :) Let me know if I need to clear anything up!

+3  A: 

This transformation has all the mentioned problems fixed:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 exclude-result-prefixes="msxsl">

    <xsl:key name="elem_key" match="elem" use="." />

    <xsl:key name="elem_key2" match="elem"
     use="concat(@key, @name, .)" />

    <xsl:variable name="all_data">
        <xsl:apply-templates select="//*[@NUM&lt;=4]">
            <xsl:sort select="name()" />
        </xsl:apply-templates>
    </xsl:variable>

    <xsl:template match="*">
        <elem key="{name()}">
            <xsl:copy-of select="@*" />
            <xsl:for-each select="@*">
                <xsl:sort select="name()" />
                <attribute>|<xsl:value-of select="name()" />|</attribute>
            </xsl:for-each>
        </elem>
    </xsl:template>

    <xsl:template match="/">
        <html>
            <body>
                <xsl:for-each select="msxsl:node-set($all_data)">
                    <xsl:for-each select=
                    "*[generate-id()
                      =
                      generate-id(key('elem_key',.)[1])
                      ]">
                        <table >
                            <tr>
                                <td>Element Name</td>
                                <xsl:for-each select="*">
                                    <td>
                                        <xsl:value-of select=
                                            "translate(.,'|','')" />
                                    </td>
                                </xsl:for-each>
                            </tr>
                            <xsl:for-each select="key('elem_key',.)">
                              <xsl:variable name="curr_elem" select="." />
                                <tr>
                                    <td>
                                        <xsl:value-of select="@key" />
                                    </td>
                                    <xsl:for-each select="*">
                                        <td >
                                            <xsl:value-of select=
                                          "$curr_elem/@*
                                               [name()
                                               =
                                                translate(current(),
                                                         '|',
                                                         ''
                                                         )
                                               ]" />
                                        </td>
                                    </xsl:for-each>
                                </tr>
                            </xsl:for-each>
                        </table>
                        <p />
                    </xsl:for-each>
                </xsl:for-each>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<pets name="myPets" NUM="2">
    <dog name="allMyDogs" NUM="5">
        <dog name="Frank" cute="yes" color="brown" type="Lab" NUM="3"/>
        <dog name="Frank" NUM="3"/>
        <dog name="Spot"  NUM="4"/>
        <dog name="Rover" cute="yes" NUM="1"/>
        <dog name="Rupert" cute="yes" type="Pug" color="black" NUM="6"/>
        <cat name="Lucy" cute="yes" NUM="4"/>
    </dog>
    <cat name="allMyCats" NUM="4">
        <cat name="Simba" cute="yes" NUM="4"/>
        <cat name="Princess" cute="no" color="black" NUM="5"/>
        <cat name="Fluffy" cute="yes" color="grey" NUM="1"/>
        <cat name="Lucy" cute="yes" color="brown" NUM="3">
            <cat name="Lucy" cute="no"  NUM="35"/>
            <cat name="Lucy" cute="yes" purrs="yes" NUM="6"/>
        </cat>
        <cat name="Lucy" cute="no" color="grey" NUM="1"/>
        <dog name="Rover" cute="yes" NUM="24"/>
    </cat>
    <cat name="Lucy" NUM="9"/>
    <dog name="Rupert Jr" cute="yes" type="Pug" color="black" NUM="0"/>
</pets>

the wanted result is produced:

<html>
    <body>
        <table>
            <tr>
                <td>Element Name</td>
                <td>cute</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>yes</td>
                <td>Lucy</td>
                <td>4</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>yes</td>
                <td>Simba</td>
                <td>4</td>
            </tr>
            <tr>
                <td>dog</td>
                <td>yes</td>
                <td>Rover</td>
                <td>1</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>allMyCats</td>
                <td>4</td>
            </tr>
            <tr>
                <td>dog</td>
                <td>Frank</td>
                <td>3</td>
            </tr>
            <tr>
                <td>dog</td>
                <td>Spot</td>
                <td>4</td>
            </tr>
            <tr>
                <td>pets</td>
                <td>myPets</td>
                <td>2</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>color</td>
                <td>cute</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>grey</td>
                <td>yes</td>
                <td>Fluffy</td>
                <td>1</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>brown</td>
                <td>yes</td>
                <td>Lucy</td>
                <td>3</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>grey</td>
                <td>no</td>
                <td>Lucy</td>
                <td>1</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>color</td>
                <td>cute</td>
                <td>name</td>
                <td>NUM</td>
                <td>type</td>
            </tr>
            <tr>
                <td>dog</td>
                <td>brown</td>
                <td>yes</td>
                <td>Frank</td>
                <td>3</td>
                <td>Lab</td>
            </tr>
            <tr>
                <td>dog</td>
                <td>black</td>
                <td>yes</td>
                <td>Rupert Jr</td>
                <td>0</td>
                <td>Pug</td>
            </tr>
        </table>
        <p></p>
    </body>
</html>
Dimitre Novatchev
@Dimitre: **hmm.. I think there are duplicates in the results.** I think there should only be 12 results back. Oh, and originally i wanted different nodes (cat vs dog) under different headings, but I think I might be ok with how you displayed it (much more concise) - I will just need to make sure it stays sorted by element name.
developer
@Dimitre: +1 for all the answer you have get from this proyect. Ja!
Alejandro
@iHeartGreek: Thanks for noticing this. I have now fixed this last problem and the transformation now produces exactly the wanted result.
Dimitre Novatchev
@Dimitre: I feel terrible saying this, but I think it is still missing one. <cat name="Lucy" cute="no" color="grey" NUM="1"/>. It doesn't seem to come back out of the deepest hierarchy.. do you know what I mean? I'm so sorry I keep fussing over this :( I'm just not good enough at XSL and XPath to recognize what needs fixing.
developer
@Dimitre: Hi again, do you think you know how to fix this last error I noticed? I am in a rush, and if not, I may have to start a bounty for someone else to answer.. but I rather not "spend" all my points on it. :( Let me know! Thanks :)
developer
@iHeartGreek: Yes, but I must find some free time.
Dimitre Novatchev
@Dimitre: Oh awesome! K that is no problem, I was just concerned that it would never get fixed.. I just don't know xslt well enough yet. I am searching for a good tutorial to learn more about key, translate, and translate.
developer
@iHeartGreek: I fixed it finally today! Please, verify that this is the result you wanted.
Dimitre Novatchev
@Dimitre: I done some testing, and I really don't think I see anything else missing! :) I hope my testing was extensive enough, but I really think it is giving the expected results now. I really can't thank you enough for your help! +1 and accepted answer doesn't seem like enough! Now my next task is trying to understand the code better so I can do more with it later on and document it! Thanks again, your help is very appreciated!
developer
@iHeartGreek: This was one of the most challenging questions on SO and it had been a pleasure for me to work on it. You are always welcome. :)
Dimitre Novatchev