Parser (FIN MT to Java)
The parser provides functionality to convert SWIFT messages FIN text into the Java message model
By doing this, you can work with SWIFT messages focusing on the data and not having to deal with low level syntax details.
The parser always performs a best effort heuristic to parse the message even if it is not well formed. All the information is read and put in a suitable object of the message model regardless of the message being SWIFT compliant.
The parser functionality has different entry point depending on the use case:
Generic processing of unknown messages
In situations where the message type is unknown, the easiest and recommended way to read and parse messages is by means
of the abstract class AbstractMT. This class provides static methods to parse an unknown message from a String or
InputStream.
For example consider the following SWIFT message example:
{1:F01BANKDEFMAXXX2039063581}{2:O1031609050901BANKDEFXAXXX89549829458949811609N}{4:
:20:007505327853
:23B:CRED
:32A:050902JPY3520000,
:33B:JPY3520000,
:50K:EUROXXXEI
:52A:FEBXXXM1
:53A:MHCXXXJT
:54A:FOOBICXX
:59:/13212312
RECEIVER NAME S.A
:70:FUTURES
:71A:SHA
:71F:EUR12,00
:71F:EUR2,34
-}
Assuming the message is in a String variable "fin", the following code will parse the FIN message into a message object object, where several getters are available to read message header information.
AbstractMT msg = AbstractMT.parse(fin);
String sender = msg.getSender();
String type = msg.getMessageType();
Even though the message type is unknown the returned object will be parsed into its specific MTnnn model object. So once
the message type is known a simple cast is enough to access detail information of the text block for reading or
manipulating its content. In the example:
System.out.println(msg.getMessageType());
if (msg.isType(103)) {
MT103 mt = (MT103) msg;
// print the message reference
System.out.println("Reference: "+mt.getField20().getValue());
// read components of a specific field
Field32A f = mt.getField32A();
Calendar date = f.getComponent1AsCalendar();
Number amount = f.getComponent3AsNumber();
}
Notice MTnnn classes provide API that is exclusively related to the fields that can be present in each message type.
Parsing an unknown message for persistence
While in the context of a parsing an unknown message, if the ultimate requirement is to read just headers information,
persist the message into a database or to process large bursts of messages, the recommended way to read and parse
messages is by means of the MtSwiftMessage class. This class also implements several ways to create the message
instance from String, InputStream; but only header information is read and the model is kept generic after the parse.
Assuming the message is in a String or InputStream variable "fin" , the following code will parse the FIN message
into a MtSwiftMessage object, and getters are used to access message header information.
MtSwiftMessage msg = MtSwftMessage.parse(fin);
String sender = msg.getSender();
String type = msg.getMessageType();
The MtSwiftMessage is a lightweight representation of the message where only the header and trailer blocks are parsed
and modeled, while the text block (the actual business payload) is kept in its raw unparsed format.
Specializing an unkown message
The above way of reading messages is the most efficient in terms of performance and it is useful to process large burst of messages; but it is limited for content manipulation.
However, once the message object is created and properly identified, it can be specialize to access the specific text block content, for example:
MT103 mt = new MT103(msg);
Field32A f = mt.getField32A();
Calendar date = f.getComponent1AsCalendar();
Number amount = f.getComponent3AsNumber();
Notice that specializing a generic message from an AbstractMT requires only a cast to the specific MTnnn, while doing
the same from an MtSwiftMessage requires a constructor.
Another important thing to know when deciding to use the AbstractMT or the MtSwiftMessage is that parsing by an
AbstractMT implies that the message type can be determined from the block 2 header, that's because internally the parser
will create the corresponding message type object (MTnnn). While parsing by an MtSwiftMessage does not impose anything
to the message content, that could be malformed or incomplete and the message object will be created anyway.
Specific processing of a known message type
When the message to parse is known, the specific model class can be used directly to parse and read the content. For example:
Where the fin parameter can be aString or InputStream with the message in its FIN format, and the parsing can be
called using the static parse method (as in the example) or by an MTnnn constructor.
Once the object is created all its specific content can be read using getters for specific sequences and fields.
Parsing message with ACK
There is a frequent misunderstanding of the FIN messages format when the actual message is preceded by a system ACK, and the expected behavior of Prowide Core parser when reading such messages.
The following example is actually a service message for ACK, followed by the original MT940 sent:
{1:F21FOOLHKH0AXXX0304009999}{4:{177:1608140809}{451:0}}{1:F01FOOLHKH0AXXX0304009999}{2:O9401609160814FOOLHKH0AXXX03040027341608141609N}{4:
:20:USD940NO1
:21:123456/DEV
:25:USD234567
:28C:1/1
:60F:C160418USD672,
:61:160827C642,S1032
:86:ANDY
:61:160827D42,S1032
:86:BANK CHARGES
:62F:C160418USD1872,
:64:C160418USD1872,
-}{5:{CHK:0FEC1E4AEC53}{TNG:}}{S:{COP:S}}
If you pass the above content to the parse method, by default the parser will get the first FIN message found in the file (the ACK service message), leaving the rest of the text into the UnparsedTextList structure.
This is just fine when reading plain user to user messages, but when the message is preceded by a service message as in the example, the resulting parsed object may not be the expected one.
When dealing with this scenario it is the user responsibility to check whether the message is a service message or not, and proceed accordingly, depending on the particular use case and application needs.
If you are trying to match and process the ACK/NAK notifications you may be interested on the service message. However, if this is the way you receive the messages from the SWIFT interface and you need the actual user message following the ACK, then you have to do something else.
The following example will parse the FIN content, check for the service id, and if it is a system message it will then gather the actual MT from the unparsed content:
SwiftMessage sm = SwiftMessage.parse(fin);
if (sm.isServiceMessage()) {
sm = SwiftMessage.parse(sm.getUnparsedTexts().getAsFINString());
}
At this point the sm variable will contain the actual user to user message, regardless if it was preceded by and ACK.
For more information on the ACK/NAK structure see Processing ACK/NAK.
Direct usage of the SwiftParser
Finally, the actual parser implementation class is another option to translate a raw message in its Java model object.
Direct usage of theSwiftParser class is discouraged but in may be useful on certain conditions where the lenient mode
of the parser must be explicitly controlled by your application.
The result of the parser is the backbone model implemented by the SwiftMessage class. So the parser will basically split the message content into the three primary elements that defines an MT message; the message itself, it's blocks, and the tag (or fields) inside each block.
Reading fields like simple tuples of name and value makes the parser very efficient, while additional content parsing can be done with the message model in a following step.
For the example, the following code can be use to gather some fields of the parsed message using the generic low level model:
String val32a = m.getBlock4().getTagValue("32A"); //get a simple value tag
String[] list71 = m.getBlock4().getTagValues("71F"); //get a repeated value tag
String with values
"EUR12,00" and "EUR2,34".
The SwiftBlock4 object basically contains an ordered List of fields found content body of the SWIFT message and provides
several API to manipulate and get the tags individually or by sub blocks. At this API level the message body can be
thought of as a text string, where instead of characters we found message fields and we can get for example all Tags
before a given one, Tags between boundaries, etc.. This API is the base foundation of the high level Sequences API.
Base64 block4 parsing
The SwiftParser provides also useful API when the message content to parse is not complete, but partial.
Several SWIFT interfaces for example provide the MT text block content encoded in base64, and without boundary brackets. For example, you might get a base64 string that represents the block 4 content of a message like this:
:20:LOW-000082027617
:28D:12345/12345
:50L:x
:50H:/x
x
:30:961123
:21:LOW-000082027617
:32B:USD1,34
:52A:CHASUS33
:59:x
:71A:SHA
:21:LOW-000082027617
:32B:USD1,34
:52A:CHASUS33
:59:x
:71A:SHA
Then in order to parse that, you can create the message and its block 4 manually like this:
SwiftMessage sm = new SwiftMessage();
sm.setBlock4(SwiftParser.parseBlock4("{4:" + payload + FINWriterVisitor.SWIFT_EOL + "-}"));
Where the payload variable contains the base64 decoded content. This will parse the block 4 content and create the all Tag elements as needed.
Then you can add the block 1 and 2 headers and optionally a block 3 and trailer block 5, also manually by creating and populating the block objects using the model.
Nested blocks in system messages
The block 4 of some system messages carries a complete message nested inside it. Instead of fields, it contains the blocks of the nested message as tags named with the block identifier, "1" to "5". The affected messages are:
- MT021 Retrieved Message (Text and History), where the retrieved message headers, text and trailers are appended to the report fields in the same block 4.
- MT096 FINCopy to Server Destination Message, where the copied message travels as a complete block 1 to 5 message.
- MT056 Logical Terminal History Report, where each login attempt in field 270 embeds a block 1 and a block 4.
For example, the following MT096 carries a copied MT103:
{1:F01FOOGCC2AAXXX0246000987}{2:O0961200070901ESASNZYYXXXX00001399900709011201S}{3:{103:AVP}{108:FOOICC2AA1234456}}{4:{1:F01FOOICC2AAXXX0123000456}{2:I103RCVRCC2AXXXXU}{3:{103:COP}{108:12345678}}{4:
:20:TRANSREF
-}{5:{MRF:070901120000070901FOOICC2AAXXX0123000456}}}{5:{CHK:017654328DEF}}
The block 4 of the parsed message contains five tags, one per nested block, and the trailers of the copied message stay within them instead of mixing with the trailers of the MT096 itself:
SwiftMessage sm = SwiftMessage.parse(fin);
SwiftBlock4 b4 = sm.getBlock4();
String block1 = b4.getTagValue("1"); // F01FOOICC2AAXXX0123000456
String block3 = b4.getTagValue("3"); // {103:COP}{108:12345678}
String block5 = b4.getTagValue("5"); // {MRF:070901120000070901FOOICC2AAXXX0123000456}
String chk = sm.getBlock5().getTagValue("CHK"); // 017654328DEF, the trailer of the MT096
Notice the curly braces within these tag values are preserved, and the message is written back unchanged with
sm.message().
For the MT056 the login attempts are not a nested block but the value of the repetitive field 270, which embeds a block 1 and an optional block 4 after the login timestamp. The parser balances the braces for this field too, so the embedded blocks are kept whole within the field value.
Rebuilding the nested message
The nested block values are the block content as expected by the single block parse methods, so the complete nested message can be rebuilt from them:
SwiftMessage copied = new SwiftMessage();
copied.addBlock(SwiftParser.parseBlock1(b4.getTagValue("1")));
copied.addBlock(SwiftParser.parseBlock2(b4.getTagValue("2")));
copied.addBlock(SwiftParser.parseBlock3(b4.getTagValue("3")));
copied.addBlock(SwiftParser.parseBlock4(b4.getTagValue("4")));
copied.addBlock(SwiftParser.parseBlock5(b4.getTagValue("5")));
The result is a regular SwiftMessage, in this example an MT103, that can be processed as any other message.
These parseBlockN methods are also useful on their own when the content to parse is a single block, for instance when
a SWIFT interface provides the header or trailer content separately. Since 10.3.20, parseBlock3 and parseBlock5
accept the block content both with the block identifier, as in {3:{103:COP}}, and without it, as in {103:COP}, which
is the form returned by the nested block tag values. A null content returns an empty block instead of failing.
Prowide Integrator users can select these nested blocks directly with path expressions, see MT Path.