Author |
Message
|
issis |
Posted: Wed Mar 28, 2012 4:44 am Post subject: ESQL references and XMLNSC & XML |
|
|
Novice
Joined: 20 Aug 2004 Posts: 17 Location: Finland
|
I encountered a weird problem with XMLNSC and references. Consider the following example that I came up with after some hours of scratching my head:
Code: |
SET OutputRoot.MQMD.Format = MQFMT_STRING;
SET OutputRoot.XMLNSC.ErrorMessage.ExceptionList.RecoverableException.RecoverableException.Text = 'dummy exception';
DECLARE rEL REFERENCE TO OutputRoot.XMLNSC.ErrorMessage.ExceptionList;
SET OutputRoot.XMLNSC.ErrorMessage.Text1 = GetLastExceptionText2(rEL);
SET OutputRoot.XML.ErrorMessage = OutputRoot.XMLNSC.ErrorMessage;
MOVE rEL TO OutputRoot.XML.ErrorMessage.ExceptionList;
SET OutputRoot.XMLNSC.ErrorMessage.Text2 = GetLastExceptionText2(rEL);
MOVE rEL TO OutputRoot.XMLNSC.ErrorMessage.ExceptionList;
SET OutputRoot.XMLNSC.ErrorMessage.Text3 = rEL.RecoverableException.RecoverableException.Text;
DELETE FIELD OutputRoot.XML;
...
CREATE FUNCTION GetLastExceptionText2(rException REFERENCE) RETURNS CHAR
...
|
The GetLastExceptionText2 is a recursive function that will fetch the "last" Text element from the ExceptionList tree. The code above produces this output:
Code: |
<ErrorMessage>
<ExceptionList>
<RecoverableException>
<RecoverableException>
<Text>dummy exception</Text>
</RecoverableException>
</RecoverableException>
</ExceptionList>
<Text2>dummy exception</Text2>
<Text3>dummy exception</Text3>
</ErrorMessage>
|
Where is Text1 ??? If I debug the ESQL, it complains that it can not find any element under the reference. However, CARDINALITY function returns 1 and the FIELDNAME gives a valid field name.
Have I understood something wrong with XMLNSC and references?
I can work around that using the second method and deleting the XML domain tree afterwards. It is just not very elegant. |
|
Back to top |
|
 |
mqjeff |
Posted: Wed Mar 28, 2012 5:04 am Post subject: |
|
|
Grand Master
Joined: 25 Jun 2008 Posts: 17447
|
Take a user trace.
It will show you exactly why GetLastExceptionText2 is returning a NULL value. |
|
Back to top |
|
 |
kimbert |
Posted: Wed Mar 28, 2012 5:32 am Post subject: |
|
|
 Jedi Council
Joined: 29 Jul 2003 Posts: 5542 Location: Southampton
|
mqjeff is correct: user trace will show you exactly what is happening in the Compute node, and why.
What is this line trying to do?
Code: |
SET OutputRoot.XML.ErrorMessage = OutputRoot.XMLNSC.ErrorMessage;
|
How did you end up in a situation where you have two output trees, one using the (deprecated ) XML parser and the other using XMLNSC? |
|
Back to top |
|
 |
Esa |
Posted: Wed Mar 28, 2012 6:10 am Post subject: |
|
|
 Grand Master
Joined: 22 May 2008 Posts: 1387 Location: Finland
|
Hi Mikko,
Something happens in GetLastExceptionText2(). Can you post the full code of the function? |
|
Back to top |
|
 |
marko.pitkanen |
Posted: Wed Mar 28, 2012 8:11 am Post subject: |
|
|
 Chevalier
Joined: 23 Jul 2008 Posts: 440 Location: Jamsa, Finland
|
Hi Issis,
In what version of the Broker you have this phenomenon?
I made testing with Windows Broker Version: 6109
With following code it seems to work?
Code: |
CREATE FUNCTION GetLastExceptionText2(rException REFERENCE) RETURNS CHAR
BEGIN
DECLARE rReference REFERENCE TO rException;
MOVE rReference TO rReference.RecoverableException;
IF LASTMOVE(rReference) THEN
RETURN GetLastExceptionText2(rReference);
END IF;
RETURN rException.Text;
END;
CREATE COMPUTE MODULE Mikko_Compute
CREATE FUNCTION Main() RETURNS BOOLEAN
BEGIN
SET OutputRoot.MQMD.Format = MQFMT_STRING;
SET OutputRoot.XMLNSC.ErrorMessage.ExceptionList.RecoverableException.RecoverableException.Text = 'dummy exception';
DECLARE rEL REFERENCE TO OutputRoot.XMLNSC.ErrorMessage.ExceptionList;
SET OutputRoot.XMLNSC.ErrorMessage.Text1 = GetLastExceptionText2(rEL);
SET OutputRoot.XML.ErrorMessage = OutputRoot.XMLNSC.ErrorMessage;
MOVE rEL TO OutputRoot.XML.ErrorMessage.ExceptionList;
SET OutputRoot.XMLNSC.ErrorMessage.Text2 = GetLastExceptionText2(rEL);
MOVE rEL TO OutputRoot.XMLNSC.ErrorMessage.ExceptionList;
SET OutputRoot.XMLNSC.ErrorMessage.Text3 = rEL.RecoverableException.RecoverableException.Text;
DELETE FIELD OutputRoot.XML;
RETURN TRUE;
|
Output was like this
Code: |
<ErrorMessage>
<ExceptionList>
<RecoverableException>
<RecoverableException>
<Text>dummy exception</Text>
</RecoverableException>
</RecoverableException>
</ExceptionList>
<Text1>dummy exception</Text1>
<Text2>dummy exception</Text2>
<Text3>dummy exception</Text3>
</ErrorMessage>
|
So there could be something wrong with your implemetation of the GetLastExceptionText2 function?
--
Marko |
|
Back to top |
|
 |
kimbert |
Posted: Wed Mar 28, 2012 8:29 am Post subject: |
|
|
 Jedi Council
Joined: 29 Jul 2003 Posts: 5542 Location: Southampton
|
I still don't understand why OutputRoot.XML is part of the solution. Anybody care to enlighten me? |
|
Back to top |
|
 |
Esa |
Posted: Wed Mar 28, 2012 8:52 am Post subject: |
|
|
 Grand Master
Joined: 22 May 2008 Posts: 1387 Location: Finland
|
Well, issis is wondering why this produces nothing in the output message:
Code: |
DECLARE rEL REFERENCE TO OutputRoot.XMLNSC.ErrorMessage.ExceptionList;
SET OutputRoot.XMLNSC.ErrorMessage.Text1 = GetLastExceptionText2(rEL);
|
But if he takes a round-trip by copying the message to another body,
the first reference miraculously starts to work, too:
Code: |
SET OutputRoot.XML.ErrorMessage = OutputRoot.XMLNSC.ErrorMessage;
MOVE rEL TO OutputRoot.XML.ErrorMessage.ExceptionList;
SET OutputRoot.XMLNSC.ErrorMessage.Text2 = GetLastExceptionText2(rEL);
MOVE rEL TO OutputRoot.XMLNSC.ErrorMessage.ExceptionList;
SET OutputRoot.XMLNSC.ErrorMessage.Text3 = rEL.RecoverableException.RecoverableException.Text; |
The difference between cases 1 and 3 is that in case 1 he DECLAREs the reference and in case 3 he MOVEs an existing reference.
My guess is that in case 1 something goes wrong and the reference is in fact pointing to OutputRoot. This could be verified by adding FIELDNAME(rEL) to the output message.
It is a good habit always to verify that the reference points to the correct element after it has been declared. I cannot see any typos in the code, but there may be some nonprinting characters that are not shown in the ESQL editor, for example?
I have to add that what issis has done is something very, very Finnish. If the problem does not seem to make any sense, you try a solution that makes even less sense. It's surprising how often this approach actually gets you closer to the real solution. I use it all the time. |
|
Back to top |
|
 |
kimbert |
Posted: Wed Mar 28, 2012 2:37 pm Post subject: |
|
|
 Jedi Council
Joined: 29 Jul 2003 Posts: 5542 Location: Southampton
|
Quote: |
It is a good habit always to verify that the reference points to the correct element after it has been declared |
True. LASTMOVE should always be checked after declaring or moving a reference. That would probably have saved the OP a lot of time. |
|
Back to top |
|
 |
issis |
Posted: Wed Mar 28, 2012 10:17 pm Post subject: |
|
|
Novice
Joined: 20 Aug 2004 Posts: 17 Location: Finland
|
Hello All!
Thank you for replies. Sorry for too short an explanation on the example. Especially omitting the function was my folly as it contained the code to control the recursion. Some answers:
- The deprecated XML is there just to show that the function does work with the similar tree structure. The function was originally coded using the XML domain.
- I left out LASTMOVE checks to keep the example code short.
- Declaring or moving the reference did not make a difference.
- I already run the user trace - it shows that the check for recursive elements fails. See below for the code.
- This was on broker version v7.0.0.1 on Windows.
I modified the function a bit so that it'll try to return the fieldname of the reference. Here is the function implementation:
Code: |
CREATE FUNCTION GetLastExceptionText2(rException REFERENCE) RETURNS CHAR
BEGIN
DECLARE cText CHAR '';
IF rException.RecoverableException IS NOT NULL THEN
SET cText = GetLastExceptionText2(rException.RecoverableException);
ELSE
SET cText = rException.Text;
END IF;
IF cText IS NULL THEN
SET cText = FIELDNAME(rException);
END IF;
RETURN cText;
END; |
With the code mentioned in the first post, that will produce a result like this:
Code: |
<ErrorMessage>
<ExceptionList>
<RecoverableException>
<RecoverableException>
<Text>dummy exception</Text>
</RecoverableException>
</RecoverableException>
</ExceptionList>
<Text1>ExceptionList</Text1>
<Text2>dummy exception</Text2>
<Text3>dummy exception</Text3>
</ErrorMessage>
|
Now.. Now that I look at this... This is not the first time I have encoutered this. In some migration projects we run into similar situation: the IS (NOT) NULL check seems to behave differently with the XML and XMLNSC trees. |
|
Back to top |
|
 |
issis |
Posted: Wed Mar 28, 2012 11:49 pm Post subject: |
|
|
Novice
Joined: 20 Aug 2004 Posts: 17 Location: Finland
|
Ok, I can make the example more simple.
XMLNSC version:
Code: |
SET OutputRoot.XMLNSC.ErrorMessage.ExceptionList.RecoverableException.Text = 'dummy text 1';
IF OutputRoot.XMLNSC.ErrorMessage.ExceptionList.RecoverableException IS NOT NULL THEN
SET OutputRoot.XMLNSC.ErrorMessage.Text1 = 'RecoverableException is not null';
ELSEIF OutputRoot.XMLNSC.ErrorMessage.ExceptionList.RecoverableException.Text IS NOT NULL THEN
SET OutputRoot.XMLNSC.ErrorMessage.Text1 = 'Text is not null';
ELSE
SET OutputRoot.XMLNSC.ErrorMessage.Text1 = 'null ?';
END IF;
|
And the result:
Code: |
<ErrorMessage>
<ExceptionList>
<RecoverableException>
<Text>dummy text 1</Text>
</RecoverableException>
</ExceptionList>
<Text1>Text is not null</Text1>
</ErrorMessage> |
The XML -domain version:
Code: |
SET OutputRoot.XML.ErrorMessage.ExceptionList.RecoverableException.Text = 'dummy text 1';
IF OutputRoot.XML.ErrorMessage.ExceptionList.RecoverableException IS NOT NULL THEN
SET OutputRoot.XML.ErrorMessage.Text1 = 'RecoverableException is not null';
ELSEIF OutputRoot.XML.ErrorMessage.ExceptionList.RecoverableException.Text IS NOT NULL THEN
SET OutputRoot.XML.ErrorMessage.Text1 = 'Text is not null';
ELSE
SET OutputRoot.XML.ErrorMessage.Text1 = 'null ?';
END IF; |
Results in this:
Code: |
<ErrorMessage>
<ExceptionList>
<RecoverableException>
<Text>dummy text 1</Text>
</RecoverableException>
</ExceptionList>
<Text1>RecoverableException is not null</Text1>
</ErrorMessage> |
|
|
Back to top |
|
 |
Esa |
Posted: Thu Mar 29, 2012 12:16 am Post subject: |
|
|
 Grand Master
Joined: 22 May 2008 Posts: 1387 Location: Finland
|
It seems XMLNSC thinks an element IS NULL if it has a null VALUE, even if it is of type XMLNSC.Folder? |
|
Back to top |
|
 |
issis |
Posted: Thu Mar 29, 2012 12:38 am Post subject: |
|
|
Novice
Joined: 20 Aug 2004 Posts: 17 Location: Finland
|
Esa wrote: |
It seems XMLNSC thinks an element IS NULL if it has a null VALUE, even if it is of type XMLNSC.Folder? |
Something like that. If I change it to "IF ....RecoverableException.Text IS NOT NULL" it works ok. |
|
Back to top |
|
 |
Esa |
Posted: Thu Mar 29, 2012 12:51 am Post subject: |
|
|
 Grand Master
Joined: 22 May 2008 Posts: 1387 Location: Finland
|
The behavior may have been changed when EXISTS function was added to the function palette. If you know that the field to be tested is a complex element, you can test it with
Code: |
IF EXISTS(OutputRoot.XMLNSC.ErrorMessage.ExceptionList.RecoverableException[]).... |
|
|
Back to top |
|
 |
issis |
Posted: Thu Mar 29, 2012 12:53 am Post subject: |
|
|
Novice
Joined: 20 Aug 2004 Posts: 17 Location: Finland
|
mqjeff wrote: |
Take a user trace.
It will show you exactly why GetLastExceptionText2 is returning a NULL value. |
I did. It said a bit different things:
1) in the recursive function there was "Failed to navigate to path element number '2' because it does not exist." This got me confused as the path was there - I had just created it before declaring the reference. It worked ok if just referred to the Text field.
2) but the latter, more simple example just stated that the RecoverableExeption element IS NULL. |
|
Back to top |
|
 |
Esa |
Posted: Thu Mar 29, 2012 4:55 am Post subject: |
|
|
 Grand Master
Joined: 22 May 2008 Posts: 1387 Location: Finland
|
Esa wrote: |
The behavior may have been changed when EXISTS function was added to the function palette. If you know that the field to be tested is a complex element, you can test it with
Code: |
IF EXISTS(OutputRoot.XMLNSC.ErrorMessage.ExceptionList.RecoverableException[]).... |
|
As a matter of fact I think you should systematically replace IS NOT NULL tests with EXISTS tests when you migrate flows from XML parser. It should do the job correctly in most cases. |
|
Back to top |
|
 |
|