MX-JSON Conversion
Back and forth conversion between MX messages and JSON
Different structures are created depending on the model object where the toJson/fromJson is invoked.
AbstractMX
For the AbstractMX
model hierarchy, the JSON structure mirrors the XML message structure. The elements preserve the
same indentation and the tags are represented as keys in the JSON structure. Thus, the actual JSON structure varies
from on message type to another.
The example below contains an XML fragment of a pacs.008.001.10 and the corresponding JSON representation, for both
the Document
and the AppHdr
tags.
XML
<?xml version="1.0" encoding="UTF-8" ?>
<RequestPayload>
<h:AppHdr xmlns:h="urn:iso:std:iso:20022:tech:xsd:head.001.001.02">
<h:Fr>
<h:FIId>
<h:FinInstnId>
<h:BICFI>AAAAUS33XXX</h:BICFI>
</h:FinInstnId>
</h:FIId>
</h:Fr>
<h:To>
<h:FIId>
<h:FinInstnId>
<h:BICFI>BBBBHKHHPBB</h:BICFI>
</h:FinInstnId>
</h:FIId>
</h:To>
<h:BizMsgIdr>REF1234567</h:BizMsgIdr>
<h:MsgDefIdr>pacs.008.001.10</h:MsgDefIdr>
<h:CreDt>2023-02-03T10:35:45-05:00</h:CreDt>
<h:PssblDplct>false</h:PssblDplct>
<h:Prty>NORM</h:Prty>
</h:AppHdr>
<Doc:Document xmlns:Doc="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.10">
<Doc:FIToFICstmrCdtTrf>
<Doc:GrpHdr>
<Doc:MsgId>0508420020FC</Doc:MsgId>
<Doc:CreDtTm>2023-02-03T10:35:45-05:00</Doc:CreDtTm>
<Doc:BtchBookg>false</Doc:BtchBookg>
<Doc:NbOfTxs>1</Doc:NbOfTxs>
<Doc:SttlmInf>
<Doc:SttlmMtd>INDA</Doc:SttlmMtd>
</Doc:SttlmInf>
<Doc:InstgAgt>
<Doc:FinInstnId>
<Doc:BICFI>AAAAUS33XXX</Doc:BICFI>
</Doc:FinInstnId>
</Doc:InstgAgt>
<Doc:InstdAgt>
<Doc:FinInstnId>
<Doc:BICFI>BBBBHKHHPBB</Doc:BICFI>
</Doc:FinInstnId>
</Doc:InstdAgt>
</Doc:GrpHdr>
<Doc:CdtTrfTxInf>
<Doc:PmtId>
<Doc:InstrId>REF1234567</Doc:InstrId>
<Doc:EndToEndId>REF1234567</Doc:EndToEndId>
<Doc:TxId>REF1234567</Doc:TxId>
<Doc:UETR>30ae21a8-9082-41cc-93ce-c71916241234</Doc:UETR>
</Doc:PmtId>
<Doc:IntrBkSttlmAmt Ccy="USD">400000.0</Doc:IntrBkSttlmAmt>
<Doc:IntrBkSttlmDt>2023-01-20</Doc:IntrBkSttlmDt>
<Doc:InstdAmt Ccy="USD">400000.0</Doc:InstdAmt>
<Doc:ChrgBr>SHAR</Doc:ChrgBr>
...
JSON
{
"fiToFICstmrCdtTrf": {
"grpHdr": {
"msgId": "0508420020FC",
"creDtTm": {
"dateTime": {
"date": {
"year": 2023,
"month": 2,
"day": 3
},
"time": {
"hour": 10,
"minute": 35,
"second": 45,
"nano": 0
}
},
"offset": {
"totalSeconds": -18000
}
},
"btchBookg": false,
"nbOfTxs": "1",
"sttlmInf": {
"sttlmMtd": "INDA"
},
"instgAgt": {
"finInstnId": {
"bicfi": "AAAAUS33XXX"
}
},
"instdAgt": {
"finInstnId": {
"bicfi": "BBBBHKHHPBB"
}
}
},
"cdtTrfTxInf": [
{
"pmtId": {
"instrId": "REF1234567",
"endToEndId": "REF1234567",
"txId": "REF1234567",
"uetr": "30ae21a8-9082-41cc-93ce-c719162444d5"
},
"intrBkSttlmAmt": {
"value": 400000.0,
"ccy": "USD"
},
"intrBkSttlmDt": {
"year": 2023,
"month": 1,
"day": 20
},
"instdAmt": {
"value": 400000.0,
"ccy": "USD"
},
"chrgBr": "SHAR",
...
]
},
"appHdr": {
"fr": {
"fiId": {
"finInstnId": {
"bicfi": "AAAAUS33XXX"
}
}
},
"to": {
"fiId": {
"finInstnId": {
"bicfi": "BBBBHKHHPBB"
}
}
},
"bizMsgIdr": "REF1234567",
"msgDefIdr": "pacs.008.001.10",
"creDt": {
"dateTime": {
"date": {
"year": 2023,
"month": 2,
"day": 3
},
"time": {
"hour": 10,
"minute": 35,
"second": 45,
"nano": 0
}
},
"offset": {
"totalSeconds": -18000
}
},
"pssblDplct": false,
"prty": "NORM",
"namespace": "urn:iso:std:iso:20022:tech:xsd:head.001.001.02"
},
"type": "MX",
"@xmlns": "urn:iso:std:iso:20022:tech:xsd:pacs.008.001.10",
"identifier": "pacs.008.001.10"
}
The only elements that contains a different representation between the XML and the JSON are the ones containing dates and time information.
For example this date and time in XML:
Is represented in JSON with a more structured format:
{
"creDtTm": {
"dateTime": {
"date": {
"year": 2023,
"month": 2,
"day": 3
},
"time": {
"hour": 10,
"minute": 35,
"second": 45,
"nano": 0
}
},
"offset": {
"totalSeconds": -18000
}
}
}
Note: If you're transitioning to version 10.x.x of the library, please note that all XML elements modelled as
XmlGregorianCalendar
in previous versions have been changed to specific classes from the java.time
package.
This change aligns with Java's shift towards more modern and efficient development practices. Ensure to update your
implementations accordingly.
Date and Time formats
This sections describes the formats used for the different date and time data types in the JSON representation.
LocalDate
If the model contains an element of type LocalDate
, the JSON representation will be:
OffsetDateTime
If the model contains an element of type OffsetDateTime
, the JSON representation will be as follows.
Without offset:
{
"dateTime":{
"date":{
"year":2019,
"month":4,
"day":28
},
"time":{
"hour":12,
"minute":13,
"second":14,
"nano":0
}
}
}
Without offset nor nanoseconds:
{
"dateTime":{
"date":{
"year":2019,
"month":4,
"day":28
},
"time":{
"hour":12,
"minute":13,
"second":14
}
}
}
Without offset, seconds nor nanoseconds:
With offset, seconds and nanoseconds:
{
"dateTime":{
"date":{
"year":2019,
"month":4,
"day":28
},
"time":{
"hour":12,
"minute":13,
"second":14,
"nano":123456789
}
},
"offset":{
"totalSeconds":"-1800"
}
}
OffsetTime
If the model contains an element of type OffsetTime
, the JSON representation will be as follows.
Without offset:
Without offset nor nanoseconds:
Without offset, seconds nor nanoseconds:
With offset, seconds and nanoseconds:
Year
If the model contains an element of type Year`, the JSON representation will be:
YearMonth
If the model contains an element of type YearMonth
, the JSON representation will be:
AbstractMX to Json conversion
In case of need to serialize the message into a JSON structure, the toJson()
method should be used.
The result will vary depending on the class model extending the AbstractMX class that's being transformed.
The code in the sample below:
MxPain00100108 mx = new MxPain00100108();
mx.setCstmrCdtTrfInitn(new CustomerCreditTransferInitiationV08());
mx.getCstmrCdtTrfInitn().setGrpHdr(new GroupHeader48());
mx.getCstmrCdtTrfInitn().getGrpHdr().setCtrlSum(new BigDecimal("1234.56"));
mx.getCstmrCdtTrfInitn().getGrpHdr().setMsgId("MYID");
OffsetDateTime offsetDateTime = OffsetDateTime.now();
mx.getCstmrCdtTrfInitn().getGrpHdr().setCreDtTm(offsetDateTime);
mx.getCstmrCdtTrfInitn().getGrpHdr().setNbOfTxs("1");
mx.getCstmrCdtTrfInitn().getGrpHdr().setInitgPty(new PartyIdentification43());
mx.getCstmrCdtTrfInitn().getGrpHdr().getInitgPty().setNm("Joe Doe");
mx.getCstmrCdtTrfInitn().getGrpHdr().getInitgPty().setCtryOfRes("USA");
mx.getCstmrCdtTrfInitn().getGrpHdr().getInitgPty().setId(new Party11Choice());
mx.getCstmrCdtTrfInitn().getGrpHdr().getInitgPty().getId().setOrgId(new OrganisationIdentification8());
mx.getCstmrCdtTrfInitn().getGrpHdr().getInitgPty().getId().getOrgId().setAnyBIC("FOOBARXX");
mx.getCstmrCdtTrfInitn().getGrpHdr().setFwdgAgt(new BranchAndFinancialInstitutionIdentification5());
mx.getCstmrCdtTrfInitn().getGrpHdr().getFwdgAgt().setBrnchId(new BranchData2());
mx.getCstmrCdtTrfInitn().getGrpHdr().getFwdgAgt().getBrnchId().setId("Ident1234");
mx.getCstmrCdtTrfInitn().getGrpHdr().getFwdgAgt().getBrnchId().setNm("A Name");
String json = mx.toJson();
System.out.println(json);
Will result in the next json message:
{
'cstmrCdtTrfInitn': {
'grpHdr': {
'msgId': 'MYID',
'creDtTm': {
'dateTime': {
'date': {
'year': 2023,
'month': 9,
'day': 4
},
'time': {
'hour': 12,
'minute': 3,
'second': 57,
'nano': 593101500
}
},
'offset': {
'totalSeconds': -10800
}
},
'nbOfTxs': '1',
'ctrlSum': 1234.56,
'initgPty': {
'nm': 'Joe Doe',
'id': {
'orgId': {
'anyBIC': 'FOOBARXX'
}
},
'ctryOfRes': 'USA'
},
'fwdgAgt': {
'brnchId': {
'id': 'Ident1234',
'nm': 'A Name'
}
}
}
},
'type': 'MX',
'@xmlns': 'urn:iso:std:iso:20022:tech:xsd:pain.001.001.08',
'identifier': 'pain.001.001.08'
}
Json to AbstractMX conversion
In case of need to convert a json formatted message into a model object, the AbstractMx#fromJson(String json)
method should be used.
The conversion will result in a concrete class of the message type that's being parsed.
Please note that casting is needed in order to access the model structure.
final String json = "{"
+ " 'fiCdtTrf': {"
+ " 'grpHdr': {"
+ " 'msgId': 'A2P76703',"
+ " 'creDtTm': {"
+ " 'dateTime': {"
+ " 'date': {"
+ " 'year': 2021,"
+ " 'month': 4,"
+ " 'day': 28"
+ " },"
+ " 'time': {"
+ " 'hour': 23,"
+ " 'minute': 35,"
+ " 'second': 53,"
+ " 'nano': 123456789"
+ " }"
+ " },"
+ " 'offset': {"
+ " 'totalSeconds': -10800"
+ " }"
+ " },"
+ " 'nbOfTxs': '1'"
+ " }"
+ " },"
+ " 'appHdr': {"
+ " 'from': {"
+ " 'type': 'BIC',"
+ " 'id': 'ABNANL20606'"
+ " },"
+ " 'to': {"
+ " 'type': 'BIC',"
+ " 'id': 'GIISIT2TXXX'"
+ " },"
+ " 'msgName': 'pacs.009.001.07',"
+ " 'msgRef': 'CPTE190421113270',"
+ " 'crDate' : {"
+ " 'dateTime': {"
+ " 'date': {"
+ " 'year': 2023,"
+ " 'month': 9,"
+ " 'day': 4"
+ " },"
+ " 'time': {"
+ " 'hour': 12,"
+ " 'minute': 3,"
+ " 'second': 57,"
+ " 'nano': 593101500"
+ " }"
+ " },"
+ " 'offset': {"
+ " 'totalSeconds': -10800"
+ " }"
+ " }"
+ " },"
+ " 'type': 'MX',"
+ " '@xmlns': 'urn:iso:std:iso:20022:tech:xsd:pacs.009.001.07',"
+ " 'identifier': 'pacs.009.001.07'"
+ "}";
AbstractMX abstractMX = AbstractMX.fromJson(json);
assertEquals("pacs.009.001.07", abstractMX.getMxId().toString());
assertEquals("CPTE190421113270", abstractMX.getAppHdr().reference());
MxPacs00900107 mx = (MxPacs00900107) abstractMX;
assertEquals("A2P76703", mx.getFICdtTrf().getGrpHdr().getMsgId());
assertEquals(
"2021-04-28T23:35:53.123456789-03:00",
mx.getFICdtTrf().getGrpHdr().getCreDtTm().toString());
assertEquals("2023-09-04T12:03:57.593101500-03:00", mx.getAppHdr().creationDate().toString());
It is also possible to use the concrete model class fromJson(String json)
method in order to avoid casting
if the json message type is already known.
String source = "{"
+ " 'sctiesSttlmTxInstr': {"
+ " 'id': {"
+ " 'creDtTm': {"
+ " 'dt': {"
+ " 'year' : 2023,"
+ " 'month' : 8,"
+ " 'day' : 11"
+ " }"
+ " },"
+ " 'nbOfTxs': '1'"
+ " }"
+ " },"
+ " 'type': 'MX',"
+ " '@xmlns': 'urn:swift:xsd:sese.023.002.01',"
+ " 'identifier': 'sese.023.002.01'"
+ "}";
MxSese02300201 mx = MxSese02300201.fromJson(source);
LocalDate dt = mx.getSctiesSttlmTxInstr().getId().getCreDtTm().getDt();
assertEquals("2023-08-11", dt.toString());
MxSwiftMessage
The MxSwiftMessage
is an alternative model structure, intended for database persistence. The model is actually a
JPA entity. Anyway, this model also provides the toJson()
and fromJson(String json)
methods for conversion to and
from JSON. The conversion can be done as follows:
MxSwiftMessage to Json conversion
In case of need to serialize the message into a JSON structure, the toJson()
method should be used.
The code in the sample below:
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<message>"
+ "<AppHdr xmlns=\"urn:swift:xsd:$ahV10\">"
+ "<From>"
+ " <Type>DN</Type>"
+ " <Id>cn=funds,o=abcdchzzwww,o=swift</Id>"
+ "</From>"
+ "<To>"
+ " <Type>DN</Type>"
+ " <Id>cn=funds,o=dcbadeff,o=swift</Id>"
+ "</To>"
+ " <MsgRef>11308917</MsgRef>"
+ " <CrDate>2013-12-23T15:50:00</CrDate>"
+ "</AppHdr>"
+ "<Document xmlns=\"urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02\" " +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
"xsi:schemaLocation=\"http://www.six-interbank-clearing.com/de/pacs.008.001.02.ch.01 pacs.008.001.02.ch.01.xsd\">"
+ "<FIToFICstmrCdtTrf>"
+ " <GrpHdr>"
+ " <MsgId>MSGID-0001</MsgId>"
+ " <CreDtTm>2001-12-17T09:30:47Z</CreDtTm>"
+ " <NbOfTxs>1</NbOfTxs>"
+ " <IntrBkSttlmDt>2012-01-25</IntrBkSttlmDt>"
+ " <SttlmInf><SttlmMtd>INDA</SttlmMtd></SttlmInf>"
+ " <InstgAgt><FinInstnId><BIC>DDDDCH20DSZ</BIC></FinInstnId></InstgAgt>"
+ " <InstdAgt><FinInstnId><BIC>DRESDEF0VNZ</BIC></FinInstnId></InstdAgt>"
+ " </GrpHdr>"
+ " <CdtTrfTxInf>"
+ " </CdtTrfTxInf>"
+ "</FIToFICstmrCdtTrf>"
+ "</Document>"
+ "</message>";
MxSwiftMessage mx = new MxSwiftMessage(xml);
String jsonMx = mx.toJson();
System.out.println(jsonMx);
Will result in the next json message:
{
'businessProcess': 'pacs',
'functionality': '008',
'variant': '001',
'version': '02',
'identifier': 'pacs.008.001.02',
'sender': 'ABCDCHZZWWW',
'receiver': 'DCBADEFFXXX',
'message': '<?xml version="1.0" encoding="UTF-8"?>
<message>
<AppHdr xmlns="urn:swift:xsd:$ahV10">
<From>
<Type>DN</Type>
<Id>cn=funds,o=abcdchzzwww,o=swift</Id>
</From>
<To>
<Type>DN</Type>
<Id>cn=funds,o=dcbadeff,o=swift</Id>
</To>
<MsgRef>11308917</MsgRef>
<CrDate>2013-12-23T15:50:00</CrDate>
</AppHdr>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.six-interbank-clearing.com/de/pacs.008.001.02.ch.01 pacs.008.001.02.ch.01.xsd">
<FIToFICstmrCdtTrf>
<GrpHdr>
<MsgId>MSGID-0001</MsgId>
<CreDtTm>2001-12-17T09:30:47Z</CreDtTm>
<NbOfTxs>1</NbOfTxs>
<IntrBkSttlmDt>2012-01-25</IntrBkSttlmDt>
<SttlmInf>
<SttlmMtd>INDA</SttlmMtd>
</SttlmInf>
<InstgAgt>
<FinInstnId>
<BIC>DDDDCH20DSZ</BIC>
</FinInstnId>
</InstgAgt>
<InstdAgt>
<FinInstnId>
<BIC>DRESDEF0VNZ</BIC>
</FinInstnId>
</InstdAgt>
</GrpHdr>
<CdtTrfTxInf/>
</FIToFICstmrCdtTrf>
</Document>
</message>',
'lastModified': {
'year': 2023,
'month': 8,
'dayOfMonth': 4,
'hourOfDay': 13,
'minute': 26,
'second': 26
},
'creationDate': {
'year': 2023,
'month': 8,
'dayOfMonth': 4,
'hourOfDay': 13,
'minute': 26,
'second': 26
},
'statusTrail': [],
'notes': [],
'properties': {},
'fileFormat': 'MX',
'reference': '11308917',
'revisions': []
}
Json to MxSwiftMessage conversion
In case of need to convert a json formatted message into a model object,
the MxSwiftMessage#fromJson(String json)
method should be used.
String json = "{"
+ " 'businessProcess': 'pacs',"
+ " 'functionality': '008',"
+ " 'variant': '001',"
+ " 'version': '02',"
+ " 'message': '<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<message>"
+ " <AppHdr xmlns:Ah=\"urn:swift:xsd:$ahV10\">"
+ " <From>"
+ " <Type>DN</Type>"
+ " <Id>cn=funds,o=abcdchzzwww,o=swift</Id>"
+ " </From>"
+ " <To>"
+ " <Type>DN</Type>"
+ " <Id>cn=funds,o=dcbadeff,o=swift</Id>"
+ " </To>"
+ " <MsgRef>11308917</MsgRef>"
+ " <CrDate>2013-12-23T15:50:00</CrDate>"
+ " </AppHdr>"
+ " <Document xmlns=\"urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02\" xmlns:xsi= \"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation= \"http://www.six-interbank-clearing.com/de/pacs.008.001.02.ch.01 pacs.008.001.02.ch.01.xsd\">"
+ " <FIToFICstmrCdtTrf>"
+ " <GrpHdr>"
+ " <MsgId>MSGID-0001</MsgId>"
+ " <CreDtTm>2001-12-17T09:30:47Z</CreDtTm>"
+ " <NbOfTxs>1</NbOfTxs>"
+ " <IntrBkSttlmDt>2012-01-25</IntrBkSttlmDt>"
+ " <SttlmInf>"
+ " <SttlmMtd>INDA</SttlmMtd>"
+ " </SttlmInf>"
+ " <InstgAgt>"
+ " <FinInstnId>"
+ " <BIC>DDDDCH20DSZ</BIC>"
+ " </FinInstnId>"
+ " </InstgAgt>"
+ " <InstdAgt>"
+ " <FinInstnId>"
+ " <BIC>DRESDEF0VNZ</BIC>"
+ " </FinInstnId>"
+ " </InstdAgt>"
+ " </GrpHdr>"
+ " <CdtTrfTxInf> </CdtTrfTxInf>"
+ " </FIToFICstmrCdtTrf>"
+ " </Document>"
+ "</message>',"
+ " 'identifier': 'pacs.008.001.02',"
+ " 'sender': 'ABCDCHZZWWW',"
+ " 'receiver': 'DCBADEFFXXX',"
+ " 'lastModified': {"
+ " 'year': 2018,"
+ " 'month': 4,"
+ " 'dayOfMonth': 18,"
+ " 'hourOfDay': 0,"
+ " 'minute': 25,"
+ " 'second': 48"
+ " },"
+ " 'creationDate': {"
+ " 'year': 2018,"
+ " 'month': 4,"
+ " 'dayOfMonth': 18,"
+ " 'hourOfDay': 0,"
+ " 'minute': 25,"
+ " 'second': 48"
+ " },"
+ " 'statusTrail': [],"
+ " 'notes': [],"
+ " 'properties': {},"
+ " 'reference': '11308917',"
+ " 'revisions': []"
+ "}";
MxSwiftMessage mx = MxSwiftMessage.fromJson(json);
assertEquals("001", mx.getVariant());
assertEquals("02", mx.getVersion());
assertEquals("008", mx.getFunctionality());
assertEquals("pacs.008.001.02", mx.getIdentifier());
assertEquals("ABCDCHZZWWW", mx.getSender());
assertEquals("DCBADEFFXXX", mx.getReceiver());
assertEquals("11308917", mx.getReference());
Calendar lastModified = mx.getLastModified();
assertEquals(2018, lastModified.get(Calendar.YEAR));
assertEquals(4, lastModified.get(Calendar.MONTH));
assertEquals(18, lastModified.get(Calendar.DAY_OF_MONTH));
assertEquals(25, lastModified.get(Calendar.MINUTE));
assertEquals(48, lastModified.get(Calendar.SECOND));