Search code examples
xmlibm-integration-busextended-sql

IIB output XML with multiple root nodes


The Problem

Given an MQ input that receives multiple XML messages, I want to output the same messages, but batched into groups of (say) 3.

e.g. Input (where each line is a new message on the input queue):

<In1/>
<In2><In2a/></In2>
<In3/>
<In4/>
<In5 test="test"/>
<In6/>

e.g. Output (where each line is a new message on the output queue):

<In1/><In2><In2a/></In2><In3/>
<In4/><In5 test="test"/><In6/>

The reason for doing this, is in order to batch the messages for transmission, in order to reduce the overhead of sending multiple small messages.

What I've tried

I've looked at this article, which suggests that using Built-in collection nodes might be an option for such a simple scenario.

I've tried creating MQInput -> Collector -> MQOutput, but the Collector node creates a special MessageCollection object, which isn't quite what I want.

So I then thought, OK, the Collector brings the messages together - now I just need to create a new message using ESQL - but I can't seem to figure out the ESQL to do it.

My current attempt:

DECLARE collection REFERENCE TO InputRoot.Collection;
MOVE collection FIRSTCHILD NAME 'MessagesIn';

DECLARE I INTEGER 1;
CREATE LASTCHILD OF OutputRoot.XMLNSC TYPE Name NAME 'Test';
WHILE LASTMOVE(collection) DO
    SET OutputRoot.XMLNSC.Test.*[I] = collection.XMLNSC.*[>];           
    MOVE collection NEXTSIBLING;
    SET I = I + 1;
END WHILE;
    
RETURN TRUE;

While this works, it only does so because it creates the single root "Test", whereas I want my output to effectively have multiple roots.

But if I remove the Test folder, this then throws errors about the output format, which is as follows:

( ['GENERICROOT' : 0x3a6d0bc8]
  (0x01000000:Name  ):Properties = ( ['MQPROPERTYPARSER' : 0x422a14d8]
    (0x03000000:NameValue):MessageSet             = NULL
    (0x03000000:NameValue):MessageType            = NULL
    (0x03000000:NameValue):MessageFormat          = NULL
    (0x03000000:NameValue):Encoding               = NULL
    (0x03000000:NameValue):CodedCharSetId         = NULL
    (0x03000000:NameValue):Transactional          = NULL
    (0x03000000:NameValue):Persistence            = NULL
    (0x03000000:NameValue):CreationTime           = NULL
    (0x03000000:NameValue):ExpirationTime         = NULL
    (0x03000000:NameValue):Priority               = NULL
    (0x03000000:NameValue):ReplyIdentifier        = NULL
    (0x03000000:NameValue):ReplyProtocol          = 'UNKNOWN' (CHARACTER)
    (0x03000000:NameValue):Topic                  = NULL
    (0x03000000:NameValue):ContentType            = NULL
    (0x03000000:NameValue):IdentitySourceType     = NULL
    (0x03000000:NameValue):IdentitySourceToken    = NULL
    (0x03000000:NameValue):IdentitySourcePassword = NULL
    (0x03000000:NameValue):IdentitySourceIssuedBy = NULL
    (0x03000000:NameValue):IdentityMappedType     = NULL
    (0x03000000:NameValue):IdentityMappedToken    = NULL
    (0x03000000:NameValue):IdentityMappedPassword = NULL
    (0x03000000:NameValue):IdentityMappedIssuedBy = NULL
  )
  (0x01000000:Folder):XMLNSC     = ( ['xmlnsc' : 0x31d412c8]
    (0x01000000:Folder):In1  = 
    (0x01000000:Folder):In2  = (
      (0x01000000:Folder):In2a = 
    )
    (0x01000000:Folder):In3  = 
  )
)

The error message reads:

        (0x03000000:NameValue):Text            = 'XML Writing Errors have occurred' (CHARACTER)
        (0x01000000:Name     ):ParserException = (
          (0x03000000:NameValue):File     = 'F:\build\slot1\S900_P\src\MTI\MTIforBroker\GenXmlParser4\ImbXMLNSCWriter.cpp' (CHARACTER)
          (0x03000000:NameValue):Line     = 976 (INTEGER)
          (0x03000000:NameValue):Function = 'ImbXMLNSCWriter::writeMisc' (CHARACTER)
          (0x03000000:NameValue):Type     = '' (CHARACTER)
          (0x03000000:NameValue):Name     = '' (CHARACTER)
          (0x03000000:NameValue):Label    = '' (CHARACTER)
          (0x03000000:NameValue):Catalog  = 'BIPmsgs' (CHARACTER)
          (0x03000000:NameValue):Severity = 3 (INTEGER)
          (0x03000000:NameValue):Number   = 5016 (INTEGER)
          (0x03000000:NameValue):Text     = 'Unexpected XML type at this point in document.' (CHARACTER)
          (0x01000000:Name     ):Insert   = (
            (0x03000000:NameValue):Type = 5 (INTEGER)
            (0x03000000:NameValue):Text = 'In2' (CHARACTER)
          )
          (0x01000000:Name     ):Insert   = (
            (0x03000000:NameValue):Type = 5 (INTEGER)
            (0x03000000:NameValue):Text = 'folderType' (CHARACTER)
          )

The Question

The parts to the question are:

  1. Is there a better way of aggregating these messages?
  2. If having multiple root elements is invalid according to the XMLNSC writer, is there another way around it? Treat it as a BLOB rather than XML, maybe?

Solution

  • The reason for doing this, is in order to batch the messages for transmission, in order to reduce the overhead of sending multiple small messages.

    Why do you believe that sending multiple small messages is a problem? Have you conducted performance tests to confirm this?

    If having multiple root elements is invalid according to the XMLNSC writer, is there another way around it?

    The XMLNSC writer tries not to write invalid XML documents, so it will always refuse to output two root tags. You will never succeed using the XMLNSC writer alone.

    You could work around this restriction by using the ESQL ASBITSTREAM function to pre-serialize the XML documents. You would need to concatenate them yourself using ESQL string functions.

    However, you are making the assumption that your XML documents will never, ever contain a line feed. You cannot be sure of that, and I think this is a dangerous thing to do.