Author |
Message
|
goffinf |
Posted: Thu Apr 05, 2007 10:05 am Post subject: Message Tree from [XML] String |
|
|
Chevalier
Joined: 05 Nov 2005 Posts: 401
|
I have an inbound message in which an XML fragment has been stored as a Base64 encoded value (its used for passing state between endpoints). It looks a bit like this :-
<Message>
<metaData>
<RequestorSavedState>
<SavedState>PE1CU2VydmljZVN0YXRlPjx .....</SavedState>
</RequestorSavedState>
</metaData>
<payload>foo</payload>
</Message>
Converting the SavedState element content back into its XML [string] representation is obviously easy enough, but is there a way that I can create part of my output message tree structure from this string (other than through crufty string manipulation). The structure is element normal form only (so no attributes to worry about) and there are no namespaces either.
Thanks
Fraser. |
|
Back to top |
|
 |
jefflowrey |
Posted: Thu Apr 05, 2007 10:06 am Post subject: |
|
|
Grand Poobah
Joined: 16 Oct 2002 Posts: 19981
|
Create Field... PARSE _________________ I am *not* the model of the modern major general. |
|
Back to top |
|
 |
kimbert |
Posted: Thu Apr 05, 2007 11:58 am Post subject: |
|
|
 Jedi Council
Joined: 29 Jul 2003 Posts: 5542 Location: Southampton
|
Jeff is correct. CREATE...PARSE will do the job. The base64 encoding introduces a small complication, though. You cannot parse a base64 character stream using an XML parser. Your options are:
- Use a Java Compute Node to decode the base64.
- Wrap the base64 stuff in a simple XML element and parse it using the MRM XML parser, and using a message set which defines the element's 'Encoding' as base64.
If it was me, I'd go for the first option. |
|
Back to top |
|
 |
marcin.kasinski |
Posted: Thu Apr 05, 2007 12:05 pm Post subject: |
|
|
Sentinel
Joined: 21 Dec 2004 Posts: 850 Location: Poland / Warsaw
|
kimbert wrote: |
- Use a Java Compute Node to decode the base64.
|
Broker can help you with it.
Use com.ibm.broker.javacompute.Base64 class to do it. _________________ Marcin |
|
Back to top |
|
 |
jefflowrey |
Posted: Thu Apr 05, 2007 12:16 pm Post subject: |
|
|
Grand Poobah
Joined: 16 Oct 2002 Posts: 19981
|
marcin.kasinski wrote: |
kimbert wrote: |
- Use a Java Compute Node to decode the base64.
|
Broker can help you with it.
Use com.ibm.broker.javacompute.Base64 class to do it. |
CREATE PROCEDURE b64Encode(IN source BLOB)
RETURNS CHARACTER
LANGUAGE JAVA
EXTERNAL NAME "com.ibm.broker.javacompute.Base64.encode";
similarly for decode. _________________ I am *not* the model of the modern major general. |
|
Back to top |
|
 |
goffinf |
Posted: Fri Apr 06, 2007 7:15 am Post subject: |
|
|
Chevalier
Joined: 05 Nov 2005 Posts: 401
|
jefflowrey wrote: |
Create Field... PARSE |
OK. but now I'm a bit confused.
According to the prototype for the CREATE function, the PARSE clause takes a bitstream expression as its first argument, so does that mean I need to decode from Base64 into CHAR and then convert this result to a BITSTREAM ??. The of course the ASBITSTREAM function requires a field reference as its first argument ??
I'm sure you guys know this stuff better than I do, but I just want to emphasis that the content of the SavedState node is CHAR data that just happens to be the string representation of an XML structure, but its is not actually *in* that structure in the message tree at that point (thats what I'm trying to create from it) :-
I also note that the ESQL reference states that the DOMAIN clause must not be used with CREATE if you also use PARSE (I guess that will be OK since the parser I am using is XMLNSC).
I tried this (a bit long hand but using a bunch of variables so I can see which steps work or not) but it didn't work. Am I way off track here ? :-
SET OutputRoot.XMLNSC.Message = InputRoot.XMLNSC.Message;
-- extract the savedState
DECLARE afheadersAsBase64 CHARACTER;
DECLARE afheadersAsString CHARACTER;
DECLARE afheadersAsBLOB BLOB;
DECLARE afheadersTemp REFERENCE TO InputRoot.XMLNSC;
SET afheadersAsBase64 = OutputRoot.XMLNSC.Message.metaData.RequestorSavedState.SavedState;
-- Decode from Base64 (this is our own eSQL function and works fine)
-- put it in a variable for now just to check
SET afheadersAsString = B64Decode(afheadersAsBase64);
-- Set the decoded value into the SavedState location
SET OutputRoot.XMLNSC.Message.metaData.RequestorSavedState.SavedState = afheadersAsString;
-- Everything's OK so far
-- String to BITSTREAM
DECLARE savedStateRef REFERENCE TO OutputRoot.XMLNSC.Message.metaData.RequestorSavedState.SavedState;
DECLARE savedStateAsBLOB BLOB ASBITSTREAM(savedStateRef, InputProperties.Encoding,InputProperties.CodedCharSetId,'','','',FolderBitStream);
-- savedStateRef DOES REFERENCE THE CHARACTER DATA WHICH IS THE XML STRING
-- BUT savedStateAsBLOB CONTAINS NO DATA AT ALL
-- Create an XML message tree fragment from the string
CREATE FIELD Environment.XMLNSC.afheadersTemp AS afheadersTemp;
-- THE NEXT LINE THROWS AN EXCEPTION (PRESUMABLY SINCE savedStateAsBLOB IS EMPTY)
CREATE FIRSTCHILD OF afheadersTemp AS afheadersTemp DOMAIN('XMLNSC') PARSE(savedStateAsBLOB, InputProperties.Encoding, InputProperties.CodedCharSetId);
Cheers
Fraser. |
|
Back to top |
|
 |
goffinf |
Posted: Fri Apr 06, 2007 10:25 am Post subject: |
|
|
Chevalier
Joined: 05 Nov 2005 Posts: 401
|
jefflowrey wrote: |
marcin.kasinski wrote: |
kimbert wrote: |
- Use a Java Compute Node to decode the base64.
|
Broker can help you with it.
Use com.ibm.broker.javacompute.Base64 class to do it. |
CREATE PROCEDURE b64Encode(IN source BLOB)
RETURNS CHARACTER
LANGUAGE JAVA
EXTERNAL NAME "com.ibm.broker.javacompute.Base64.encode";
similarly for decode. |
Cool. Sorry for being a bit thick, but can I use the eSQL just as you have it here, that is, do I actually need a javacompute node in my flow. |
|
Back to top |
|
 |
marcin.kasinski |
Posted: Fri Apr 06, 2007 12:08 pm Post subject: |
|
|
Sentinel
Joined: 21 Dec 2004 Posts: 850 Location: Poland / Warsaw
|
goffinf wrote: |
Sorry for being a bit thick, but can I use the eSQL just as you have it here, that is,
|
Yes you can.
goffinf wrote: |
do I actually need a javacompute node in my flow. |
You don't need this node in your flow.
Just try this ESQL code. _________________ Marcin |
|
Back to top |
|
 |
kimbert |
Posted: Fri Apr 06, 2007 12:33 pm Post subject: |
|
|
 Jedi Council
Joined: 29 Jul 2003 Posts: 5542 Location: Southampton
|
hi gofinf,
You're mostly doing the right things, but the details matter ( as you've found out). Hopefully this will help...
Quote: |
does that mean I need to decode from Base64 into CHAR |
Yes
Quote: |
and then convert this result to a BITSTREAM |
The input to CREATE...PARSE is a BLOB, so you need to CAST the resulting CHARACTER data to a BLOB.
Quote: |
The of course the ASBITSTREAM function requires a field reference as its first argument ?? |
Correct. What's strange about that? ASBITSTREAM does the exact opposite of CREATE...PARSE. It creates a BLOB from a message tree.
Quote: |
I also note that the ESQL reference states that the DOMAIN clause must not be used with CREATE if you also use PARSE |
Where does it say that? You must use the domain clause when you use CREATE...PARSE. How else will WMB know which parser to use?
Quote: |
it didn't work. Am I way off track here ? |
Not too far off track. I would do it this way:
- Decode the base64 date to a CHARACTER field
- CAST the character field to a BLOB
- CREATE LASTCHILD OF <somwhere> DOMAIN "XMLNSC" PARSE <the BLOB> CCSID <from input message> ENCODING <from input message>
You don't need to use ASBITSTREAM anywhere. All you need is a BLOB, and you can get that using CAST.
By the way, re:
Code: |
-- Create an XML message tree fragment from the string
CREATE FIELD Environment.XMLNSC.afheadersTemp AS afheadersTemp; |
...I hope that Environment.XMLNSC was created using the Domain clause. |
|
Back to top |
|
 |
goffinf |
Posted: Mon Apr 09, 2007 9:16 am Post subject: |
|
|
Chevalier
Joined: 05 Nov 2005 Posts: 401
|
kimbert wrote: |
You're mostly doing the right things, but the details matter ( as you've found out). Hopefully this will help... |
Thanks, it helped a lot.
goffinf wrote: |
I also note that the ESQL reference states that the DOMAIN clause must not be used with CREATE if you also use PARSE |
[quote="kimbert]Where does it say that? You must use the domain clause when you use CREATE...PARSE. How else will WMB know which parser to use?[/quote]
The 'Notes' under the railroad diagram for the CREATE statement say :-
Notes:
1. Do not use the DomainClause and ParseClause with the FIELD qualifier.
But it didn't matter because I didn't use CREATE FIELD anyway, as per your advice
kimbert wrote: |
I would do it this way:
- Decode the base64 date to a CHARACTER field
- CAST the character field to a BLOB
- CREATE LASTCHILD OF <somwhere> DOMAIN "XMLNSC" PARSE <the BLOB> CCSID <from input message> ENCODING <from input message>
|
Thanks that worked OK. I did (and possibly still do) have one problem though. When the message comes into my flow there is no value for either the encoding or CCSID (these flows use a custom input node so I wasn't surprised). Passing these empty values causes the CREATE LASTCHILD to exception, so I ended up having to hard code in 1208 for CCSID (we are only using XML and using UTF-8 encoding) and 546 for the ENCODING value. Is this going to be a problem when I deploy this flow onto a non Windows platform ?? Is there a better approach ??
kimbert wrote: |
By the way, re:
Code: |
-- Create an XML message tree fragment from the string
CREATE FIELD Environment.XMLNSC.afheadersTemp AS afheadersTemp; |
...I hope that Environment.XMLNSC was created using the Domain clause. |
Yeh sorry I didn't explain that bit. It Environment.XMLNSC is what we internally call our XML 'scratch-pad' and it *is* created using the XMLNSC Domain.
Regards
Fraser. |
|
Back to top |
|
 |
|