Wrox Programmer Forums

Need to download code?

View our list of code downloads.

Go Back   Wrox Programmer Forums > XML > XSLT
Password Reminder
Register
| FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of tens of thousands of software programmers and website developers including Wrox book authors and readers. As a guest, you can read any forum posting. By joining today you can post your own programming questions, respond to other developers’ questions, and eliminate the ads that are displayed to guests. Registration is fast, simple and absolutely free .
DRM-free e-books 300x50
Reply
 
Thread Tools Search this Thread Display Modes
  #1 (permalink)  
Old May 30th, 2005, 03:08 PM
Registered User
 
Join Date: May 2005
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default Add attribute to the parent after checking child

Hi,

I have this XML shown below.
<?xml version="1.0"?>
<MESSAGE_SUMMARY>
<IN_TO_XYZ>
<COMPLETE>25 </COMPLETE>
<RUNNING>30 </RUNNING>
</IN_TO_XYZ>
<IN_TO_PQR>
<COMPLETE>26 </COMPLETE>
<RUNNING>31 </RUNNING>
</IN_TO_PQR>
</MESSAGE_SUMMARY>


Now I want to check if the RUNNING value is > 20. If it is then add an attribute to the parent of that RUNNING element as ALERT="TRUE" MSG="COUNT TOO HIGH".

Now when I try to add the element ALERT as shown below, it does not work and it gives me the error "XTDE0420: Attribute nodes must be created before the children of an element node". I am usin SAXON parser.

Here is my XSL. Any hint please..

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="iso-8859-1" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>

<xsl:template match="//*[RUNNING]">
<xsl:if test=".[RUNNING > 10]">
<xsl:attribute name="alert">true</xsl:attribute>
</xsl:if>
<xsl:call-template name="copy-all"/>
</xsl:template>

<xsl:template name="copy-all" match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Any help is appreciated.

Kiran


Reply With Quote
  #2 (permalink)  
Old May 30th, 2005, 03:52 PM
mhkay's Avatar
Wrox Author
Points: 18,487, Level: 59
Points: 18,487, Level: 59 Points: 18,487, Level: 59 Points: 18,487, Level: 59
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

XSLT is designed so that the result tree can be produced in document order; this means there is a rule that you can't add attributes to an element after you have added children to the element (the rule isn't expressed in this way, because there's no notion of time, but it amounts to the same thing.)

Have you tried running this with the -T tracing option? It gives you more information about what's going on.

However, I have to say I found this one quite tricky to debug myself. What's happening is that the match="*" template for <MESSAGE_SUMMARY> is first copying the whitespace text node immediately after the start tag; then when it hits the IN_TO_XYZ element it's firing the rule that creates the attribute node, and you can't create an attribute after creating a text node, even if the text node is all whitespace.

If you fix this, for example by stripping whitespace, you still hit the problem later on with the <IN_TO_PQR> element.

I wonder what output you were actually trying to create? Was it deliberate that the IN_TO_XYZ and IN_TO_PQR elements aren't being copied, and that the alert attribute are (both!) being added to the MESSAGE_SUMMARY element?

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
Reply With Quote
  #3 (permalink)  
Old May 30th, 2005, 04:26 PM
Registered User
 
Join Date: May 2005
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Michael,

 Thanks for replying. I am not really clear of your question. Are you asking what is the reason for having the requirement like that??

 Also I still am at a loss as to how to achive what I am trying to do. Is there any other way I can do that will let me achive add an attribute??
I guess what I am looking at is "How to manipulate attributes based on child value".

All over the internet they keep talking about how to manipulate children based on attribute.. ;-((

Any Help is greatly appreciated.

PS: I also went through your book XSLT programmers reference( author is Michaell Kay) and it does not talk about that..

Reply With Quote
  #4 (permalink)  
Old May 30th, 2005, 04:29 PM
Registered User
 
Join Date: May 2005
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Here is the trace I get when I run with -T option..

*********START**************************
C:\XSL\myxsl>java -jar ../saxon8.jar -T msgsummary.xml msgsummary.xsl
Warning: Running an XSLT 1.0 stylesheet with an XSLT 2.0 processor
<trace saxon-version="8.4" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <source node="/" line="0" file="msgsummary.xml">
  <xsl:template match="/" line="4" module="msgsummary.xsl">
   <xsl:apply-templates line="5" module="msgsummary.xsl">
    <source node="/MESSAGE_SUMMARY" line="2" file="msgsummary.xml">
     <xsl:template name="copy-all" match="*" line="15" module="msgsummary.xsl">
      <xsl:copy line="16" module="msgsummary.xsl">
       <xsl:copy-of select="@*" line="17" module="msgsummary.xsl">
       </xsl:copy-of>
       <xsl:apply-templates line="18" module="msgsummary.xsl">
        <source node="/MESSAGE_SUMMARY/IN_TO_TOC[1]" line="3" file="msgsummary.
         <xsl:template name="copy-all" match="*" line="15" module="msgsummary.x
          <xsl:copy line="16" module="msgsummary.xsl">
           <xsl:copy-of select="@*" line="17" module="msgsummary.xsl">
           </xsl:copy-of>
           <xsl:apply-templates line="18" module="msgsummary.xsl">
            <source node="/MESSAGE_SUMMARY/IN_TO_TOC[1]/COMPLETE[1]" line="4" f

             <xsl:template name="copy-all" match="*" line="15" module="msgsumma
              <xsl:copy line="16" module="msgsummary.xsl">
               <xsl:copy-of select="@*" line="17" module="msgsummary.xsl">
               </xsl:copy-of>
               <xsl:apply-templates line="18" module="msgsummary.xsl">
               </xsl:apply-templates>
              </xsl:copy>
             </xsl:template>
            </source>
            <source node="/MESSAGE_SUMMARY/IN_TO_TOC[1]/RUNNING[1]" line="5" fi
             <xsl:template name="copy-all" match="*" line="15" module="msgsumma
              <xsl:copy line="16" module="msgsummary.xsl">
               <xsl:copy-of select="@*" line="17" module="msgsummary.xsl">
               </xsl:copy-of>
               <xsl:apply-templates line="18" module="msgsummary.xsl">
               </xsl:apply-templates>
              </xsl:copy>
             </xsl:template>
            </source>
           </xsl:apply-templates>
          </xsl:copy>
         </xsl:template>
        </source>
        <source node="/MESSAGE_SUMMARY/IN_TO_ATI[1]" line="7" file="msgsummary.
         <xsl:template name="copy-all" match="*" line="15" module="msgsummary.x
          <xsl:copy line="16" module="msgsummary.xsl">
           <xsl:copy-of select="@*" line="17" module="msgsummary.xsl">
           </xsl:copy-of>
           <xsl:apply-templates line="18" module="msgsummary.xsl">
            <source node="/MESSAGE_SUMMARY/IN_TO_ATI[1]/COMPLETE[1]" line="8" f

             <xsl:template name="copy-all" match="*" line="15" module="msgsumma
              <xsl:copy line="16" module="msgsummary.xsl">
               <xsl:copy-of select="@*" line="17" module="msgsummary.xsl">
               </xsl:copy-of>
               <xsl:apply-templates line="18" module="msgsummary.xsl">
               </xsl:apply-templates>
              </xsl:copy>
             </xsl:template>
            </source>
            <source node="/MESSAGE_SUMMARY/IN_TO_ATI[1]/RUNNING[1]" line="9" fi
             <xsl:template name="copy-all" match="*" line="15" module="msgsumma
              <xsl:copy line="16" module="msgsummary.xsl">
               <xsl:copy-of select="@*" line="17" module="msgsummary.xsl">
               </xsl:copy-of>
               <xsl:apply-templates line="18" module="msgsummary.xsl">
               </xsl:apply-templates>
              </xsl:copy>
             </xsl:template>
            </source>
           </xsl:apply-templates>
          </xsl:copy>
         </xsl:template>
        </source>
       </xsl:apply-templates>
      </xsl:copy>
     </xsl:template>
    </source>
   </xsl:apply-templates>
  </xsl:template>
 </source>
</trace>
<?xml version="1.0" encoding="iso-8859-1"?>
<MESSAGE_SUMMARY>
   <IN_TO_TOC>
              <COMPLETE>25 </COMPLETE>
              <RUNNING>30 </RUNNING>
   </IN_TO_TOC>
   <IN_TO_ATI>
              <COMPLETE>26 </COMPLETE>
              <RUNNING>31 </RUNNING>
   </IN_TO_ATI>
</MESSAGE_SUMMARY>


********END*********************************

Reply With Quote
  #5 (permalink)  
Old May 30th, 2005, 04:36 PM
Registered User
 
Join Date: May 2005
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default

I re-read your reply, I am sorry I did not get it first time.. Here is the output I am trying to create..

<?xml version="1.0"?>
<MESSAGE_SUMMARY>
<IN_TO_XYZ alert="true" msg="count too high">
    <COMPLETE>25 </COMPLETE>
    <RUNNING>30 </RUNNING>
</IN_TO_XYZ>
<IN_TO_PQR alert="true" msg="count too high">
    <COMPLETE>26 </COMPLETE>
    <RUNNING>31 </RUNNING>
</IN_TO_PQR>
</MESSAGE_SUMMARY>


My intention is not to add the alert to msg_summary rather to INTO_XYZ or INTO_PQR elements..

Sorry again
Kiran

Reply With Quote
  #6 (permalink)  
Old May 30th, 2005, 04:40 PM
mhkay's Avatar
Wrox Author
Points: 18,487, Level: 59
Points: 18,487, Level: 59 Points: 18,487, Level: 59 Points: 18,487, Level: 59
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

If you can describe the output you want to achieve, it will be easier for me to tell you where you are going wrong.

A useful tip with XSLT is that your code always needs to be structured according to the structure of the output, not the structure of the input. Don't try to generate an attribute when you hit the information in the source file that causes that attribute to be output; generate it while you are constructing the element that owns it.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
Reply With Quote
  #7 (permalink)  
Old May 30th, 2005, 04:42 PM
mhkay's Avatar
Wrox Author
Points: 18,487, Level: 59
Points: 18,487, Level: 59 Points: 18,487, Level: 59 Points: 18,487, Level: 59
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

This trace seems to be from a different stylesheet or a different source document - it never tries to create the offending attribute node.


Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
Reply With Quote
  #8 (permalink)  
Old May 30th, 2005, 05:01 PM
mhkay's Avatar
Wrox Author
Points: 18,487, Level: 59
Points: 18,487, Level: 59 Points: 18,487, Level: 59 Points: 18,487, Level: 59
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

In that case your template

<xsl:template match="//*[RUNNING]">
<xsl:if test=".[RUNNING > 10]">
<xsl:attribute name="alert">true</xsl:attribute>
</xsl:if>
<xsl:call-template name="copy-all"/>
</xsl:template>

is trying to create the attribute before it has created the element to which it is to be attached!

Change it to:

<xsl:template match="*[RUNNING]">
<xsl:copy>
  <xsl:if test="RUNNING > 10">
    <xsl:attribute name="alert">true</xsl:attribute>
  </xsl:if>
  <xsl:copy-of select="@*"/>
  <xsl:apply-templates/>
</xsl:copy>
</xsl:template>

(I simplified your match pattern and test condition - yours were not wrong, just over-complicated.)

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
Reply With Quote
  #9 (permalink)  
Old May 30th, 2005, 05:29 PM
Registered User
 
Join Date: May 2005
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Michael,

 You are awesome. So I guess I should start the copy before I test the condition. That was my mistake is that correct.

One more small request. I changed the XSL 'lil bit to get the COMPLETE and RUNNING nodes to be copied to output but it is givein is as below..

<?xml version="1.0" encoding="iso-8859-1"?>
<IN_TO_XYZ alert="true">
           <COMPLETE>25 </COMPLETE>
           <RUNNING>30 </RUNNING>

        25
        30
</IN_TO_XYZ>
<IN_TO_PQR alert="true">
           <COMPLETE>26 </COMPLETE>
           <RUNNING>31 </RUNNING>

        26
        31
</IN_TO_PQR>

as you can see it is printing the extra information out side nodes..

Here is my modified ( not much) XSL..

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="iso-8859-1" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>

<xsl:template match="*[RUNNING]">
<xsl:copy>
  <xsl:if test="RUNNING > 10">
    <xsl:attribute name="alert">true</xsl:attribute>
  </xsl:if>
  <xsl:copy-of select="@*|node()"/>
  <xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>

Thanks again for all the help..
Kiran


Reply With Quote
  #10 (permalink)  
Old May 30th, 2005, 05:32 PM
mhkay's Avatar
Wrox Author
Points: 18,487, Level: 59
Points: 18,487, Level: 59 Points: 18,487, Level: 59 Points: 18,487, Level: 59
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

  <xsl:copy-of select="@*|node()"/>
  <xsl:apply-templates select="@*|node()"/>

First you copy the children, then you apply-templates to them, which copies them again.


Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
checking for a child? gabster XSLT 2 March 7th, 2007 02:18 PM
parent child connect s2mo SQL Server 2005 1 February 20th, 2007 07:16 AM
parent child presentation db96s1 Beginning PHP 0 February 7th, 2006 06:19 PM
Parent - Child Combo babloo81 BOOK: Professional Jakarta Struts 0 April 27th, 2005 01:54 PM



All times are GMT -4. The time now is 06:02 PM.


Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.