471,066 Members | 1,230 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,066 software developers and data experts.

Interesting XML traversing problem

I've been struggling too long with this problem and will revere anyone as a god if they can figure it out.

I have this xml:
Expand|Select|Wrap|Line Numbers
  1. <tasks>
  2.      <task id="t1"/>
  3.      <task id="t2">
  4.           <folderid>one</folderid>
  5.      </task>
  6.      <task id="t3">
  7.           <note>just some content here</note>
  8.           <folderid>two</folderid>
  9.      </task>
  10.      <task id="t4">
  11.           <folderid>two</folderid>
  12.           <folderid>three</folderid>
  13.           <folderid>four</folderid>
  14.      </task>
  15.      <task id="t5">
  16.           <folderid>four</folderid>
  17.      </task>
  18.      <task id="t6"/>
  19.           <note>blah blah blah</note>
  20.      </task>
  21.      <task id="t7"/>
  22. </tasks>
  23.  
And I want this result html:
Expand|Select|Wrap|Line Numbers
  1. <div id="root">
  2.      task t1
  3.      task t6 - blah blah blah
  4.      task t7
  5. </div>
  6. <div id="one">
  7.      task t2
  8. </div>
  9. <div id="two">
  10.      task t3 - just some content here
  11.      task t4
  12. </div>
  13. <div id="three">
  14.      task t4
  15. </div>
  16. <div id="four">
  17.      task t4
  18.      task t5
  19. </div>
  20.  
- each task may have 0 to many "folderid" elements
- each folder id will have it's own div
- each div section will list those tasks that have that folderid
- if a task has no folderid sibling, it is put into the "root" div
- each task can only have one note child element
Oct 14 '08 #1
8 1579
Dormilich
8,656 Expert Mod 8TB
- each task may have 0 to many "folderid" elements
- each folder id will have it's own div
- each div section will list those tasks that have that folderid
- if a task has no folderid sibling, it is put into the "root" div
- each task can only have one note child element
problem 1: your xml is not well-formed (missing root element and xml declaration)
problem 2: please use code tags [code] when posting code

the last requirement will need a DTD or similar for validation (though you could catch such an exception)

the most complicated thing is to get the <div>s right. my proposal - get all <folderid>s (<xsl:for-each>), sort them (<xsl:sort>) and create a new <div> when the content changes.

if you show me what you've done so far, I will gladly re- and advise you.

regards
Oct 14 '08 #2
jkmyoung
2,057 Expert 2GB
Tasks without folder children:
task/[not(folderid)]

Assuming you're using XSLT 1.0 and you have a task template already:
to get the other tasks, you'll probably use something like Muenchian Grouping:
http://www.jenitennison.com/xslt/gro...muenchian.html

Expand|Select|Wrap|Line Numbers
  1. <div id="root">
  2.   <xsl:apply-templates select="task/[not(folderid)]"/>
  3.   <xsl:for-each select="//folderid[not(current() = preceding::folderid]">
  4.   <!-- now you have unique folderids -->
  5.     <div id="{.}">
  6.       <xsl:apply-templates select="//folderid[current() = .]/.."/>
  7.        <!-- get parent task node -->
  8.     </div>
  9.   </xsl:for-each>
  10. </div>
  11.  
Oct 14 '08 #3
Sorry it took so long to get reply. Been a long day at work.

I left the root element and xml declaration out by mistake. I thought it would be easier to ask for help by dummying down the XML. In order to show what I've done so far, I'll post the code as-is.

My XML:
Expand|Select|Wrap|Line Numbers
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <?xml-stylesheet type="text/xsl" href="..\myxsl.xsl"?>
  3. <root version="3">
  4.     <context>
  5.         <folders>
  6.             <folder id="970f67719995f688" name="Inbox" icon="inbox"/>
  7.             <folder id="3a6bd2e5fe4b7b0a" name="Personal" icon="home">
  8.                             <folder id="492e514a76b05a3a" name="Longterm" icon="person"/>
  9.             </folder>
  10.             <folder id="2dab99b095a73877" name="Office" icon="office"/>
  11.             <folder id="734059758a6a0f83" name="Groceries" icon="cart">
  12.                             <folder id="129e2ed6cd372e1f" name="Drinks" icon="cart"/>
  13.                             <folder id="a485f0318e18e0e3" name="Party" icon="music"/>
  14.             </folder>
  15.             <folder id="6c74b7b4eceb796f" name="Very-Important" icon="info"/>
  16.             <folder id="826ceb631b4e7039" name="Web"/>
  17.             <folder id="8c32ad3f8a34db4c" name="XMas"/>
  18.         </folders>
  19.         <tasks>
  20.                 <task id="9cda311cb7351fff" desc="remember the milk"/>
  21.                 <task id="c82f392f4f21dc56" desc="focus on everything"/>
  22.                 <task id="1873b622ff99598f" desc="call Susan">
  23.                                 <folder>3a6bd2e5fe4b7b0a</folder>
  24.                 </task>
  25.                 <task id="0955b7d4fa5b9dfd" desc="research options for participating in north pole expedition">
  26.                                 <note>(before it&apos;'s too late! ;)</note>
  27.                                 <folder>492e514a76b05a3a</folder>
  28.                 </task>
  29.                 <task id="db947856a257c6cf" desc="email Frank (gym thing)">
  30.                                 <folder>970f67719995f688</folder>
  31.                 </task>
  32.                 <task id="4c69ff58577cb622" desc="think about my long-term prospects at ACME">
  33.                                 <folder>2dab99b095a73877</folder>
  34.                 </task>
  35.                 <task id="9699da44e2d7439d" desc="tell Lumbergh what I really think of him">
  36.                                 <folder>2dab99b095a73877</folder>
  37.                 </task>
  38.                 <task id="b0caf9f07c206c69" desc="demolish that HP printer!!!!">
  39.                                 <folder>2dab99b095a73877</folder>
  40.                                 <folder>6c74b7b4eceb796f</folder>
  41.                 </task>
  42.                 <task id="afe68a1c545b9172" desc="buy cheese">
  43.                                 <folder>734059758a6a0f83</folder>
  44.                 </task>
  45.                 <task id="04e9ecce043c46b1" desc="buy spam">
  46.                                 <folder>734059758a6a0f83</folder>
  47.                 </task>
  48.                 <task id="301126e402623bb0" desc="buy lettuce for Susan">
  49.                                 <folder>734059758a6a0f83</folder>
  50.                 </task>
  51.                 <task id="5ab1e5e579a0f7c7" desc="buy this German beer Frank was talking about">
  52.                                 <note>think it was called &quot;"Tannenzäpfle&quot;"</note>
  53.                                 <folder>129e2ed6cd372e1f</folder>
  54.                 </task>
  55.                 <task id="73e4a64f7845856a" desc="buy finger-food">
  56.                                 <folder>a485f0318e18e0e3</folder>
  57.                 </task>
  58.                 <task id="26af69c4e916321e" desc="book flight to Tonga">
  59.                                 <folder>3a6bd2e5fe4b7b0a</folder>
  60.                                 <folder>8c32ad3f8a34db4c</folder>
  61.                 </task>
  62.                 <task id="c43bfd24bd5817af" desc="sign up for lis.to newsletter">
  63.                                 <note>http://lis.to/</note>
  64.                                 <folder>970f67719995f688</folder>
  65.                 </task>
  66.  
  67.             </tasks>
  68.     </context>
  69. </root>
  70.  
My XSL:
Expand|Select|Wrap|Line Numbers
  1. <?xml version="1.0"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3. <xsl:template match="/root/context">
  4. <html>
  5.     <head>
  6.         <title>My Lists</title>
  7.  
  8.     </head>
  9.     <body>
  10.  
  11.     <!-- build divs -->
  12.  
  13.         <div class="tabs-container">
  14.             <xsl:for-each select="//folders//*">
  15.                 <xsl:call-template name="GetTasksByFolderTest">
  16.                                 <xsl:with-param name="FolderId" select="@id"/>
  17.                 </xsl:call-template>
  18.             </xsl:for-each>
  19.             <!--Below is an example of html div's I want-->
  20.             <!--<div class="tab" id="tab1">
  21.                 <h2>number 1</h2>
  22.                 <p>put some crap here</p>
  23.             </div>
  24.             <div class="tab" id="tab2">
  25.                 <h2>number 2</h2>
  26.                 <p>put some crap here too</p>
  27.             </div>
  28.             <div class="tab" id="tab3">
  29.                 <h2>number 3</h2>
  30.                 <p>and more here for tab 3</p>
  31.             </div>
  32.             <div class="tab" id="tab4">
  33.                 <h2>number 4</h2>
  34.                 <p>and yeah put something here for 4</p>
  35.             </div>
  36.             <div class="tab" id="test">
  37.                 <h2>number test</h2>
  38.                 <p>and yeah put something here for test</p>
  39.             </div>-->
  40.         </div><!-- end divs -->
  41.     </body>
  42. </html>
  43. </xsl:template>
  44.  
  45.  
  46. <xsl:template name="GetTasksByFolderTest">
  47. <xsl:param name="FolderId"/>
  48. <xsl:for-each select="//tasks//*">
  49.     <xsl:choose>
  50.         <xsl:when test="$FolderId = @id">
  51.             <xsl:value-of select="@desc"/>
  52.         </xsl:when>
  53.     </xsl:choose>
  54. </xsl:for-each>
  55.     <div class="tab" id="{$FolderId}">
  56.         <h2>
  57.             <xsl:value-of select="$FolderId"/> | <xsl:value-of select="@id"/>
  58.         </h2>            
  59.     </div>
  60. </xsl:template>
  61. </xsl:stylesheet>
  62.  
I can't really change the XML as it's coming from a third-party.


----------------
problem 1: your xml is not well-formed (missing root element and xml declaration)
problem 2: please use code tags [code] when posting code

the last requirement will need a DTD or similar for validation (though you could catch such an exception)

the most complicated thing is to get the <div>s right. my proposal - get all <folderid>s (<xsl:for-each>), sort them (<xsl:sort>) and create a new <div> when the content changes.

if you show me what you've done so far, I will gladly re- and advise you.

regards
Oct 15 '08 #4
I am playing around with this code but can't quite seem to get it to work.

I put it in my Task template and using Cooktop xml I get the error "Keyword xsl:apply-template may not be used here."

Not being very well versed in XSLT, I find reading this code a bit difficult. :(
I'll keep playing around and see if I can eventually figure out how it works.

Thanks.

-------
Tasks without folder children:
task/[not(folderid)]

Assuming you're using XSLT 1.0 and you have a task template already:
to get the other tasks, you'll probably use something like Muenchian Grouping:
http://www.jenitennison.com/xslt/gro...muenchian.html

Expand|Select|Wrap|Line Numbers
  1. <div id="root">
  2.   <xsl:apply-template select="task/[not(folderid)]"/>
  3.   <xsl:for-each select="//folderid[not(current() = preceding::folderid]">
  4.   <!-- now you have unique folderids -->
  5.     <div id="{.}">
  6.       <xsl:apply-templates select="//folderid[current() = .]/.."/>
  7.        <!-- get parent task node -->
  8.     </div>
  9.   </xsl:for-each>
  10. </div>
  11.  
Oct 15 '08 #5
Dormilich
8,656 Expert Mod 8TB
got some basic code working:
Expand|Select|Wrap|Line Numbers
  1. <?xml version="1.0" encoding="ISO-8859-1" ?> 
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3.  
  4. <xsl:output method="html" indent="yes"/>
  5.  
  6. <xsl:template match="/">
  7. <html>
  8. <body>
  9. // I don't know why but apply-templates doesn't get all
  10.     <xsl:for-each select="//folder[@id]">
  11.         <div id="{@id}">
  12.             <xsl:call-template name="xyz"/>
  13.         </div>
  14.     </xsl:for-each>
  15. </body>
  16. </html>
  17. </xsl:template>
  18.  
  19. <xsl:template name="xyz">
  20.     <xsl:variable name="id" select="@id"/>
  21.     <xsl:for-each select="//task[folder = $id]">
  22. task: <xsl:value-of select="@id"/>,
  23. // or whatever code comes here
  24.     </xsl:for-each>
  25. </xsl:template>
  26.  
  27. </xsl:stylesheet>
I skipped the "root" div and the check for the <note> child, but it makes the divs as wanted.

regards

Am I a god now? *gg*
Oct 15 '08 #6
jkmyoung
2,057 Expert 2GB
Accidentally deleted the 's' when posting from apply-templates
Oct 15 '08 #7
Beautiful! Yes, you are a god. Honestly I don't know how some of you XSLT gurus got to where you are. It's a heck of a language to learn.

I added the extra bits of code I needed and got it working exactly as envisioned.

Thank you so much!!!



got some basic code working:
Expand|Select|Wrap|Line Numbers
  1. <?xml version="1.0" encoding="ISO-8859-1" ?> 
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3.  
  4. <xsl:output method="html" indent="yes"/>
  5.  
  6. <xsl:template match="/">
  7. <html>
  8. <body>
  9. // I don't know why but apply-templates doesn't get all
  10.     <xsl:for-each select="//folder[@id]">
  11.         <div id="{@id}">
  12.             <xsl:call-template name="xyz"/>
  13.         </div>
  14.     </xsl:for-each>
  15. </body>
  16. </html>
  17. </xsl:template>
  18.  
  19. <xsl:template name="xyz">
  20.     <xsl:variable name="id" select="@id"/>
  21.     <xsl:for-each select="//task[folder = $id]">
  22. task: <xsl:value-of select="@id"/>,
  23. // or whatever code comes here
  24.     </xsl:for-each>
  25. </xsl:template>
  26.  
  27. </xsl:stylesheet>
I skipped the "root" div and the check for the <note> child, but it makes the divs as wanted.

regards

Am I a god now? *gg*
Oct 16 '08 #8
Dormilich
8,656 Expert Mod 8TB
Beautiful! Yes, you are a god. Honestly I don't know how some of you XSLT gurus got to where you are. It's a heck of a language to learn.
we got there by exercise. for me it was working on my website which uses an awful lot of xml (because I'm too lazy to write static html includes) and somehow the xml has to be displayed as html....

I don't find xsl complicated to learn since you have the standard loop and condition constucts like every other programming lanuage. for me the most difficult part (still) is getting the XPath expressions right (... I need to spend more time at my xsl article, *sigh* ...)

btw. your actual xml was much easier to use than your simplified sample

regards

and don't give up

your personal xsl deity
Oct 16 '08 #9

Post your reply

Sign in to post your reply or Sign up for a free account.

Similar topics

3 posts views Thread by Plamen Valtchev | last post: by
1 post views Thread by alfred | last post: by
4 posts views Thread by plmanikandan | last post: by
4 posts views Thread by A_StClaire_ | last post: by
30 posts views Thread by asit | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.