Background
Swift has long advised on the use of namespace prefixes in ISO 20022 XML messages. The summary being, these are XML messages so treat them as XML, not text. A key feature of XML is the use of namespaces and namespace prefixes. This is nothing to do with Swift or CBPR+, just the XML standard. For some, this still comes as a surprise and likely causes problems.
If you are unfamiliar with namespace prefixes or Swift’s advice, please follow these links:
- CBPR+ ISO message processing with XML namespace prefix
- ISO 20022 for Financial Institutions (the new version of the previous ISO 20022 Customer Adoption Guide)
“Any financial institution may send a valid MX document, correctly specifying the relevant namespaces in any of the way allowed by the XML standard.”
Using the BIC of a Debtor, all the following namespace prefixes would be valid:
<pacs:Dbtr>
<pacs:Id>
<pacs:OrgId>
<pacs:AnyBIC>AETCUS33</pacs:AnyBIC>
</pacs:OrgId>
</pacs:Id>
</pacs:Dbtr>
<doc:Dbtr>
<doc:Id>
<doc:OrgId>
<doc:AnyBIC>AETCUS33</doc:AnyBIC>
</doc:OrgId>
</doc:Id>
</doc:Dbtr>
And any other prefix could be received. No prefix is also valid:
<Dbtr>
<Id>
<OrgId>
<AnyBIC>AETCUS33</AnyBIC>
</OrgId>
</Id>
</Dbtr>
TL;DR
The short version of this post; if you are struggling with differing namespace prefixes when processing inbound MX messages, follow this 2-step solution:
- Treat and parse them as XML using an XML parser.
- Using XPath, use local-name to ignore the prefix.
Thanks for your attention!
The Longer Version
Now, the longer version; it is not uncommon for initial ISO 20022 implementations to be minimum viable product; the minimum effort to switch to generating and consuming MX rather than MT – but specifically minimising wholesale changes to back-end systems. Given dedicated MT parsers won’t work on MX messages, the MVP approach may not have implemented XML parsing, rather treating MX messages as text. This usually involves the extensive use of complex regular expressions (regex). Differing namespace prefixes, which will be present in MX messages sent by other agents and over which you have no control, will cause issues.
MX messages are XML; treat them as such by implementing XML parsers and employing XPath and XQuery using local-name. Saxon[1], a popular XML parser, widely embedded in middleware applications, offers a simple solution using a wildcard character:
Example:
Assume, for monitoring and screening purposes, you need to extract the Debtor BIC (if it exists) in all MT 103 messages received.
In this MT 103 you would have been parsing the second line in field 50A, after the optional account line – VISTUS31, in this example:
In the MX world, in a pacs.008.001.08, you are now looking for the BIC in this path:
/Document/FIToFICstmrCdtTrf/CdtTrfTxInf/Dbtr/Id/OrgId/AnyBIC
If a regex had been employed to do this previously in MT 103s, it may well be an amended regex being used to extract the BIC in the pacs.008.
You may have legacy systems built around parsing text messages or other reasons. Whatever they may be, now is the time rectify the issue. Treating XML messages as XML requires the use of an XML parser which enables the use of the XML language features, specifically XPath and XQuery, and thereby supporting the correct handling of namespaces and namespace prefixes.
Examples in XPath & XQuery
Examples below using local-name (using XMLSpy), to find and extract the specific element, ignoring namespace prefix.
Extracting the Debtor BIC:
//*/*[local-name() = 'Dbtr']/*[local-name() = 'Id']/*[local-name() = 'OrgId']/*[local-name() = 'AnyBIC']/text()
Extacting the Creditor Name:
//*/*[local-name() = 'Cdtr']/*[local-name() = 'Nm']/text()
Extracting a contatenation of Creditor postal address elements (Street Name, Town Name and Country):
concat(//*/*[local-name() = 'Cdtr']/*[local-name() = 'PstlAdr']/*[local-name() = 'StrtNm'],', ',//*/*[local-name() = 'Cdtr']/*[local-name() = 'PstlAdr']/*[local-name() = 'TwnNm'],', ',//*/*[local-name() = 'Cdtr']/*[local-name() = 'PstlAdr']/*[local-name() = 'Ctry'])
Examples in Python (lXML)
dbtr_bic = mx_msg.xpath('//*[local-name() = $dbtr]/*[local-name() = $id]/*[local-name() = $orgid]/*[local-name() = $anybic]/text()', dbtr='Dbtr', id='Id', orgid='OrgId', anybic='AnyBic')
Where:
mx_msg = etree.fromstring(msg)
# msg being a UTF-8 encoded string of the MX message
Example in Java (javax)
Similar capabilities exist in other languages, such as Java (using javax).
// Define the XQuery expression
String expression = "//*[local-name() = 'Dbtr']/*[local-name() = 'Id']/*[local-name() = 'OrgId']/*[local-name() = 'AnyBic']/text()";
XPathExpression expr = xpath.compile(expression);
// Execute the XQuery expression
NodeList blockNodes = (NodeList) expr.evaluate(document, XPathConstants.NODESET);
Summary
Regardless of the namespace prefix, by treating MX messages as XML, impementing an XML parser and using XPath and XQuery with local name, you can extract the data needed. Whilst there are other considerations with this approach, such as performance, it remains a versatile, simple and pragmatic solution.
[1] https://www.saxonica.com/documentation12/index.html#!xpath-api