Parser (XML to Java)
The parser's main usage is to access the business content of the MX message from a business oriented Java model instead of dealing with the underlying xml structure.
Therefore the parser allows the conversion of an XML message into a Java model that represents the message and provides specific getters to retrieve the elements read from the XML.
It is important to remark that the parser will only accept well structured XML files (all tags properly closed) and it will only read the portions according to the corresponding MX message, meaning any unexpected xml content will be dropped. Also to remark, the parser does not perform any content validation regarding tags repetitions, charsets, qualifiers or semantics.
There are several entry points for the MX parsing feature depending on the use case:
Parsing a specific known message type
When the specific MX message type is known a specific MX message object can be created from String
or InputStream
, as
follows:
The above code is also available in constructors.
This is the more efficient way to read the message if the specific version is known in advance. The parser will read into the output object both the Document and the AppHdr (if present).
The implementation is based on the JAXB2 unmarshaller.
Parsing an unknown message type
When the specific category and version of the message to read is unknown you can use the base calss of the message hierarchy to do the parsing, then cast if necessary to extract specific data.
AbstractMX mx = AbstractMX.parse(xml);
if ("camt.048.001.03".equals(mx.getMxId().id())) {
MxCamt04800103 camt = (MxCamt04800103) mx;
System.out.println("Message id: " + camt.getModfyRsvatn().getMsgHdr().getMsgId());
}
The MX message model classes, such as MxPacs00800108
, that extend the AbstractMX
are designed to parse and create
specific message types.
The AbstractMX
is simply the parent class of the model hierarchy, and it is abstract. Thus, it is not intended to
create messages from scratch, and in fact, you cannot create a new empty instance of it. It is only created in parse
method calls.
If you parse an XML with a specific model class, any element in the XML that does not belong to the specific schema is automatically dropped. This is because the model classes are designed for specific types. This means that you cannot parse an element such as /Document/GrpHdr/InstrctdAgt into a model if that model does not define such a path or element.
When you parse an XML with the AbstractMX
, the API internally autodetects the message type from the namespace and parses
the XML with a specific model class. The AbstractMX
is just a way to process messages without autodetecting or creating
specific instances yourself.
Parsing many unknown message types
When you need to parse many unknown message types and just store the content in a database or read generic metadata
that is common to all messages (such as the message type, sender, receiver and reference) you can use the generic
MxSwiftMessage
class for parsing:
The class implements several constructors and static methods to create the instance from different sources; String, InputStream, AbstractMX, etc...
Only the header will be parsed in order into structured attributes, while the plain input XML will be stored as a String.
This model object is also suited for persistence because it can be stored in a simple common table for all message types.
If you need to read specific attributes not available in the generic metadata, you can use the specific classes such as
MxSese02300201
to read specific data.
Reading specific attributes from many message types
Finally, you have yet another option if you need to read many attributes (not available in the MtSwiftMessage
metadata) from
many known or unknown message types. This is an alternative, low-level approach where the model objects are not used,
and the underlying XML structure is directly traversed.
This MxNode
is a generic, lightweight, and fast XML tree representation that can be used to traverse or build any XML.
You can think of it as a simplified DOM. It lacks several features of a full DOM but is much faster and lighter.
The MxNode
model and all its methods are part of a generic XML API. Its purpose is to provide an easy-to-use, generic XML
handling API to avoid using Java's native APIs such as DOM, SAX, Stax, etc.
The MxNode
model is not specific to ISO 20022. You can use it to parse or create any XML structure.
In particular, you can use the MxNode
model to generically parse or create MX messages. However, the model itself does
not impose any restrictions on what you can do.
Performance: JaxbContext cache
The parse methods available in the specific classes such as MxCamt00300104
or in the AbstractMX
class uses JAXB
unmarshaller. By default implementation will create a new JAXBContext
for each parse invocation. While this is fine when
you need to parse a low number of messages it might become a considerable performance issue for larger volumes.
To deal with it the implementation supports injecting a cache for the JaxbContext
.
This is managed by a singleton class JaxbContextLoader
like this:
JAXBContext
will be cached and re-used between parse
and write calls. Meaning if you parse a sese.023.002.01, the first time the context will be initialized, but afterwards
each time you parse another sese.023.002.01 the available context will be reused.
The JaxbContextCacheImpl
is a default out-of-the-box cache implementation based on a simple ConcurrentHashMap
, with
no eviction. This implementation is aimed to avoid additional third party dependencies. Meaning it can be used right
away and it is a fair solution for the performance issue. However, since it has no eviction, if you process a wide
variety of message types the cache will grow, consuming a lot of memory.
For a more robust solution You can easily implement your own cache with Guava, Ehcache, Caffeine or any other cache library. For instance if your application already has the Guava library, you can write a Guava based cache like this:
public class GuavaJaxbContextCache implements JaxbContextCache {
private final Cache<Class<? extends AbstractMX>, JAXBContext> cache = CacheBuilder.newBuilder().maximumSize(100).build();
public JAXBContext get(final Class<? extends AbstractMX> messageClass, final Class<?>[] classes) throws ExecutionException {
return cache.get(messageClass, () -> JAXBContext.newInstance(classes));
}
}
And then inject this implementation to the loader with:
The Guava based example code is available at: https://github.com/prowide/prowide-iso20022-examples/