2

EDIT: By removing xmlns I tried to solve a problem when Mapping Node wouldn't parse Input Message with fields that contain namespace. Input Body is set manually - not from XSD. When namespaces are removed from file manually everything works. But when I do use ESQL script it stops working for some uknown to me reason.

Hello I have XML message from which I need to remove namespaces. What I am trying to do is recursively walk trough XML tree and remove xmlns attribute if exists. Unfortunatelly when I'm trying to do SET element = NULL my loop won't go to the next element MOVE element NEXTSIBLING. I tried to do DELETE FIELD element but that gives same effect.

Here's my full code:

CREATE COMPUTE MODULE test_Compute1
CREATE FUNCTION Main() RETURNS BOOLEAN
BEGIN
     DECLARE blobMsg BLOB Environment.BLOB.BLOB ;
     CREATE LASTCHILD OF Environment.Variables.inpMsg DOMAIN ('XMLNSC') NAME 'XMLNSC';
     CREATE LASTCHILD OF Environment.Variables.inpMsg.XMLNSC PARSE(blobMsg OPTIONS FolderBitStream CCSID InputRoot.Properties.CodedCharSetId FORMAT 'XMLNSC');
     SET Environment.Variables.statusRes.statusCode = Environment.Variables.inpMsg.XMLNSC.errorResponse.httpCode;
     SET Environment.Variables.statusRes.detail = Environment.Variables.inpMsg.XMLNSC.errorResponse.httpMessage;
     SET Environment.Variables.statusRes.additionalStatus.detail = Environment.Variables.inpMsg.XMLNSC.errorResponse.moreInformation;
     CALL NavigateTree(Environment.Variables.inpMsg.XMLNSC);
    RETURN TRUE;
END;

CREATE PROCEDURE NavigateTree(IN root REFERENCE)
BEGIN
    DECLARE element REFERENCE TO root;
    DECLARE hint CHARACTER;
    DECLARE test CHARACTER;
    SET test = '';
    SET hint = '';

    MOVE element FIRSTCHILD;
        -----------
    IF LASTMOVE(element) THEN
        SET hint = 'has children';
    ELSE
    IF FIELDNAME(element) = 'xmlns' THEN
        DELETE FIELD element;
    END IF;
    END IF;

    WHILE LASTMOVE(element) DO
        -- not working awell:
        -- DECLARE space1 NAMESPACE 'namespace1';
        -- SET element.(XML.NamespaceDecl)* = NULL;
        DECLARE nameField2 CHARACTER FIELDNAMESPACE(element);
        DECLARE nameField CHARACTER FIELDNAME(element);
        DECLARE ifhint CHARACTER;

        CALL NavigateTree(element);
        MOVE element NEXTSIBLING;
    END WHILE;
    SET hint = 'finished';

END;
   END MODULE;

Do you have any ideas how can I do that?

EDIT:

CREATE COMPUTE MODULE test_Compute1
    CREATE FUNCTION Main() RETURNS BOOLEAN
    BEGIN
         DECLARE blobMsg BLOB Environment.BLOB.BLOB ;
         CREATE LASTCHILD OF Environment.Variables.inpMsg DOMAIN ('XMLNSC') NAME 'XMLNSC';
         CREATE LASTCHILD OF Environment.Variables.inpMsg.XMLNSC PARSE(blobMsg OPTIONS FolderBitStream CCSID InputRoot.Properties.CodedCharSetId FORMAT 'XMLNSC');
         SET Environment.Variables.statusRes.statusCode = Environment.Variables.inpMsg.XMLNSC.errorResponse.httpCode;
         SET Environment.Variables.statusRes.detail = Environment.Variables.inpMsg.XMLNSC.errorResponse.httpMessage;
         SET Environment.Variables.statusRes.additionalStatus.detail = Environment.Variables.inpMsg.XMLNSC.errorResponse.moreInformation;
         CALL StripNamespaces(Environment.Variables.inpMsg);
        RETURN TRUE;
    END;
    CREATE PROCEDURE StripNamespaces(IN fieldRef REFERENCE)
    BEGIN
        IF FIELDTYPE(fieldRef) IN (XMLNSC.NamespaceDecl, XMLNSC.SingleNamespaceDecl) THEN
            DELETE FIELD fieldRef;
            RETURN;
        END IF;
        DECLARE childRef REFERENCE TO fieldRef;
        MOVE childRef FIRSTCHILD;
        WHILE LASTMOVE(childRef) DO
            DECLARE currentChildRef REFERENCE TO childRef;
            MOVE childRef NEXTSIBLING;
            CALL StripNamespaces(currentChildRef);
        END WHILE;
    END;
END MODULE;

XML that is working as an input file that goes to mapping node (manually edited):

<?xml version="1.0" encoding="utf-8"?>
<Receive >
    <messageData>
        <CD >
            <EXP  />
            <EXAMPLE  />
        </CD>
        <XRP >
            <EX1>
                <SEG>string</SEG>
                <SEG2>integer</SEG2>
            </EX1>
            <ARRAY>
                <AR1>string</AR1>
                <AR2 />
            </ARRAY>
            <ARRAY>
                <AR1>integer</AR1>
                <AR2 />
            </ARRAY>
        </XRP>
    </messageData>
</Receive>

XML that is not working (mapping node can't process it properly after it goes trough parsing mentioned above):

<?xml version="1.0" encoding="utf-8"?>
<Receive xmlns="namespace">
    <messageData>
        <CD xmlns="namespace2">
            <EXP xmlns="namespace3" />
            <EXAMPLE xmlns="namespace3" />
        </CD>
        <XRP xmlns="namespace2">
            <EX1>
                <SEG>string</SEG>
                <SEG2>integer</SEG2>
            </EX1>
            <ARRAY>
                <AR1>string</AR1>
                <AR2 />
            </ARRAY>
            <ARRAY>
                <AR1>integer</AR1>
                <AR2 />
            </ARRAY>
        </XRP>
    </messageData>
</Receive>

In both cases debugger shows same tree structure after going trough same parser:

Variables
            inpMsg
                    XMLNSC
                            Receive
                                    messageData
                                            CD
                                                    EXP
                                                    EXAMPLE
                                            XRP
                                                    EX1
                                                            SEG:CHARACTER:string
                                                            SEG2:CHARACTER:integer
                                                    ARRAY
                                                            AR1:CHARACTER:string
                                                            AR2
                                                    ARRAY
                                                            AR1:CHARACTER:integer
                                                            AR2
TwistedOwl
  • 1,195
  • 1
  • 17
  • 30
  • 2
    The two message trees are not the same, but the debugger does not display the namespaces of the XML tags, so they _look_ the same. For this reason, when namespaces are in play I often use a Trace node instead. – kimbert Jan 16 '20 at 15:47
  • @kimbert I had such assumptions for a while that debugger might not show everything and now I have confirmation. I will check with Trace node and see what I find. Thanks! – TwistedOwl Jan 16 '20 at 15:49
  • 1
    Also...if you are determined not to create an XSD then the Mapping node is probably the wrong choice for your mapping logic. Just use ESQL instead - it will be just as clear. – kimbert Jan 16 '20 at 15:50
  • ESQL is not an option (for now) because XML files have more fields and require some processing. That would be a lot of writing I guess. Sounds like I have to try writing XSD. – TwistedOwl Jan 16 '20 at 15:54

1 Answers1

3

After deletion of the field you have to leave the recursive function.

This is how we do it:

CREATE PROCEDURE StripNamespaces(IN fieldRef REFERENCE)
BEGIN
    IF FIELDTYPE(fieldRef) IN (XMLNSC.NamespaceDecl, XMLNSC.SingleNamespaceDecl) THEN
        DELETE FIELD fieldRef;
        RETURN;
    ELSEIF FIELDNAMESPACE(fieldRef) <> '' THEN
        SET fieldRef NAMESPACE = '';
    END IF;
    DECLARE childRef REFERENCE TO fieldRef;
    MOVE childRef FIRSTCHILD;
    WHILE LASTMOVE(childRef) DO
        DECLARE currentChildRef REFERENCE TO childRef;
        MOVE childRef NEXTSIBLING;
        CALL StripNamespaces(currentChildRef);
    END WHILE;
END;
Daniel Steinmann
  • 2,119
  • 2
  • 15
  • 25
  • Thank you for your reply. This method works. For some uknown reason my Mapping node won't read such tree anyway but if I remove xmlns manually from input xml file Mapping reads input values from environment variable properly. Note that I set Input in Mapping node manually. Do you have any ideas why would that happen? Thanks. – TwistedOwl Jan 16 '20 at 10:51
  • 1
    Without knowing what `Environment.BLOB.BLOB` is and seeing the involved XSDs it is hard to tell. But I can assure you that the Mapping Node can handle namespaces well. You can can add a [Trace Node](https://www.ibm.com/support/knowledgecenter/en/SSMKHH_10.0.0/com.ibm.etools.mft.doc/ac16810_.htm) in front of the Mapping Node to see the exact input message tree. – Daniel Steinmann Jan 16 '20 at 11:16
  • ```Environment.BLOB.BLOB``` is a BLOB received from Input File. I don't have any XSD - I expected that I can set up Mapping Input manually and Mapping Node will read my Environment properly (```Environment.Variable``` is used as an Input for Mapping Node). I think I could try writing my own XSD but right now trying everything to avoid that. As I said it's weird that if I remove namespaces **manually** from xml file my mapping node reads Env variable properly and if I remove them using procedures - it's not. – TwistedOwl Jan 16 '20 at 11:56
  • 1
    So your input file is a single XML document, right? Why is it in Environment and not InputRoot? Can you share then content of the XML file in the question? – Daniel Steinmann Jan 16 '20 at 14:24
  • Just noticed that using Environment causes some side effects (old data stays in memory and is being processed in next run lol). Yes my file is a single XML document. I have edited main post so I believe it should be more clear now. Sorry for not mentioning it earlier. – TwistedOwl Jan 16 '20 at 15:23
  • I found out that this procedure is removing only namespace declaration ```(0x03000102:NamespaceDecl):xmlns = 'namespace2' (CHARACTER)``` but other field still have ```(0x03000000:PCDataField)namespace2:SEG = 'string' (CHARACTER)``` namespace afterall. Is there any way to remove namespace? – TwistedOwl Jan 16 '20 at 17:46
  • 1
    You're are right, I was not being carefully what I answered and therefore corrected my answer. – Daniel Steinmann Jan 16 '20 at 21:33
  • Thanks Daniel, you are the best! Spent whole day trying to figure out what happens there and came into same solution. @kimbert thank you too :) – TwistedOwl Jan 16 '20 at 21:43
  • You must have interpreted your test results incorrectly. The lifetime of the Environment tree is the same as the lifetime of the flow invocation. No data will be preserved for the next flow run. You _can_ put data into a SHARED ROW variable in order to cache it, or use a persistent counter. Is that what you meant? – kimbert Jan 17 '20 at 09:15
  • 1
    re: removing the namespace...the StripNamespaces procedure needs to explicitly set the namespace to the empty string on _every single node in the message tree_. It is not sufficient to just remove the namespace declarations. – kimbert Jan 17 '20 at 09:19