Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
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 software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old May 30th, 2005, 03:08 PM
Registered User
 
Join Date: May 2005
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


 
Old May 30th, 2005, 03:52 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
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
 
Old May 30th, 2005, 04:26 PM
Registered User
 
Join Date: May 2005
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..

 
Old May 30th, 2005, 04:29 PM
Registered User
 
Join Date: May 2005
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*********************************

 
Old May 30th, 2005, 04:36 PM
Registered User
 
Join Date: May 2005
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

 
Old May 30th, 2005, 04:40 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
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
 
Old May 30th, 2005, 04:42 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
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
 
Old May 30th, 2005, 05:01 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
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
 
Old May 30th, 2005, 05:29 PM
Registered User
 
Join Date: May 2005
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


 
Old May 30th, 2005, 05:32 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
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





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





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