Generating human readable SWIFT messages using Prowide-Core? - java

I'm using Prowide-Core for SWIFT message handling. At the moment I use the JSON methods to produce human readable versions of my messages if I need to dump them out. This produces output like this:
...
}, {
"97A" : ":SAFE//0123-0123456-55-000"
}, {
...
I.e. I get alphanumeric codes, like 97A, for the field names.
However it I look at Field97A.java:471 I can see that it (and all other fields) know their more human readable names (Qualifier and Account in the case of 97A).
I've tried looking for some toString() like method that makes use of this information to produce something even more readable than the JSON output but haven't found anything yet.
Is there such a method on SwiftMessage or one of the related classes? Or has someone written something nice that can traverse a message and print it out using the information returned by getComponentLabels() etc?

I'm one of the authors. For future reference, in the upcoming SRU2018 release (scheduled for October) we have revamped all the JSON API in all model abstraction layers.
The toJson in SwiftMessage object still produces plain name/value tuples for the Tags. However, in the MTnnn classes, the toJson uses Field to produce JSON with business labels such as:
{ "name": "90A",
"qualifier": "DEAL",
"percentageTypeCode": "PRCT",
"price": "102,713552"
},
{ "name": "36B",
"qualifier": "ESTT",
"quantityTypeCode": "AMOR",
"quantity": "7999999,573"
},
{ "name": "97A",
"qualifier": "SAFE",
"account": "0123-0123456-55formatted
}
Besides the JSON, you can iterate the fields and print formated name and values with the available getLabel and getValueDisplay methods.
For example:
Locale locale = Locale.getDefault();
SwiftMessage sm = SwiftMessage.parse("{1:F01BACOARB1A0B20000000000}{2:I103ADRBNL21XXXXU2}{3:{108:FOOB3926BE868XXX}}{4:\n" +
":20:REFERENCE\n" +
":23B:CRED\n" +
":32A:180730USD1234567,89\n" +
":50A:/12345678901234567890\n" +
"CFIMHKH1XXX\n" +
":59:/12345678901234567890\n" +
"JOE DOE\n" +
"MyStreet 1234\n" +
":71A:OUR\n" +
"-}");
System.out.println("Sender: " + sm.getSender());
System.out.println("Receiver: " + sm.getReceiver() + "\n");
for (Tag tag : sm.getBlock4().getTags()) {
Field field = tag.asField();
System.out.println(Field.getLabel(field.getName(), "103", null, locale));
System.out.println(field.getValueDisplay(locale) + "\n");
}
Will produce this output:
Sender: BACOARB1A0B2
Receiver: ADRBNL21XXXX
Sender's Reference
REFERENCE
Bank Operation Code
CRED
Value Date/Currency/Interbank Settled Amount
Jul 30, 2018 USD 1,234,567.89
Ordering Customer
12345678901234567890 CFIMHKH1XXX
Beneficiary Customer
12345678901234567890 JOE DOE MyStreet 1234
Details of Charges
OUR
Where components are split and formatted for the locale.
And if you also need labels per component, you can further iterate the components like this:
for (Tag tag : sm.getBlock4().getTags()) {
Field field = tag.asField();
System.out.println("\n" + Field.getLabel(field.getName(), "103", null, locale));
for (int component = 1 ; component <= field.componentsSize() ; component++) {
if (field.getComponent(component) != null) {
System.out.print(field.getComponentLabel(component) + ": ");
System.out.println(field.getValueDisplay(component, locale));
}
}
}
Pruducing this other output:
Sender's Reference
Reference: REFERENCE
Bank Operation Code
Type: CRED
Value Date/Currency/Interbank Settled Amount
Date: Jul 30, 2018
Currency: USD
Amount: 1,234,567.89
Ordering Customer
Account: 12345678901234567890
BIC: CFIMHKH1XXX
Beneficiary Customer
Account: 12345678901234567890
Name And Address: JOE DOE
Name And Address 2: MyStreet 1234
Details of Charges
Code: OUR
Finally, if you are interested, the Integrator library from Prowide includes out-of-the-box printout visitors to produce HTML, TXT, and XML including structured sequences and BIC expanded with the institution information. You may ask Prowide for a trial.

SwiftTagListBlock provides a toJson method that iterates over Tag objects:
public String toJson() {
final StringBuilder sb = new StringBuilder();
sb.append("[ \n");
if (this.tags != null && !this.tags.isEmpty()) {
for (int i=0;i<this.tags.size();i++) {
final Tag t = this.tags.get(i);
sb.append("{ \"").append(t.getName()).append("\" : \"").append(escapeJson(t.getValue())).append("\" }");
if (i+1<this.tags.size()) {
sb.append(',');
}
sb.append('\n');
}
}
sb.append("]");
return sb.toString();
}
You could tweak the source code and call the asField() method of Tag, in order to have access to the Field representation and use the information required for your output.

Related

How to detect if gson.fromjson() has excess elements

I'm currently making a class that tests if the gson classes I generated are still up to date, this means I have to detect if any of the json elements have changed, deleted or added. Let's say I have a json which is simply
{
"FloatingText": {
"Dodge_Enabled": false,
"foo": false
},
"General": {
"something": false,
"somethingelse": false
}
}
And the class for floatingtext has the following fields:
#SerializedName("Dodge_Enabled")
#Expose
public Boolean dodgeEnabled;
Now the foo field is obviously missing here, my question is how to find out how I can detect this reliably even when the json is about a thousand times bigger. The gson documentation states that it should throw a exception when the json doesn't match the class but it doesn't and after some research it only throws it when the type is heavily malformed so that's off the table.
The code I have now checks correctly for missing or changed fields but not for new ones
GameSettings gameSettings = gson.fromJson(json, GameSettings.class);
for (Field declaredField : gameSettings.getClass().getDeclaredFields()) {
declaredField.setAccessible(true);
Object object = declaredField.get(gameSettings);
if (object == null && !Modifier.isStatic(declaredField.getModifiers())) {
Assert.fail();
}
for (Field declaredSubfield : object.getClass().getDeclaredFields()) {
declaredSubfield.setAccessible(true);
Object subObject = declaredSubfield.get(object);
if (subObject == null && !Modifier.isStatic(declaredSubfield.getModifiers())) {
System.out.println("Processing went wrong at: " + declaredSubfield + "\n Json: " + json);
Assert.fail();
}
}
}
One thing you could do is to deserialize the incoming JSON, then serialize it again and check if the result matches your incoming JSON:
GameSettings settings = gson.fromJson( json, GameSettings.class );
String exportedJson = gson.toJson( settings );
if ( !exportedJson.equals( json ) )
{
Assert.fail();
}
This would of course be prone to raise alarms if someone modified the previously exported JSON by hand and changed something about the pretty-printing. But if it's just about raising an alert for the dev to check, it might be enough.
If you wanted to output more information about what exactly is different in the external representation, you could use a library that detects diffs:
https://github.com/java-diff-utils/java-diff-utils
https://github.com/google/diff-match-patch

Cloudant With Lucene Search Fails To Sort As Expected

I am pretty new to Cloudant but have developed in SQL on DB2 for some time. I am running into an issue where I *think I am using the Lucene query engine and Cloudant indexes to return results from my query. The query gets all the results I want however, they are not sorted correctly. I am wanting to sort the results alphabetically based on the "officialName" field. Because we are only returning the first 21 out of n results (and then we have a js handler to call more results via paging) we cannot sort in the java side but must do so via Cloudant. Our application is running Java and executed using IBM's Bluemix and WebSphere Liberty Profile. I have packaged the cloudant-client-2.8.0.jar and cloudant-HTTP-2.8.0.jar files to access the Cloudant database. We have many queries that are working so the connection itself is fine.
Here is the code that builds the Cloudant Client search object:
Search search = getCloudantDbForOurApp().search("bySearchPP-ddoc/bySearchPP-indx").includeDocs(true);
SearchResult<DeliverableDetails> result = search.sort(getSortJsonString(searchString)).querySearchResult(getSearchQuery(searchString), DeliverableDetails.class);
Here is the method getSortJsonString. It should be noted that the search string is typically NOT null. I should also note that leaving in or taking out the -score attribute does effect the search but never achieves alpha sorted results.
private String getSortJsonString(String searchString) {
String sortJson;
if (searchString != null && !searchString.isEmpty()) {
sortJson = "[\"-<score>\",\"officialName<string>\"]";
} else {
sortJson = "\"officialName<string>\"";
}
return sortJson;
}
Here is the getSearchQuery method's relevant code for reference:
...
query += "(";
query += "officialName:" + searchString + "^3";
query += " OR " + "deliverableName:" + searchString + "^3";
query += " OR " + "alias:" + searchString + "^3";
query += " OR " + "contact:" + searchString;
query += ")";
....
// The query will look like below, where<search_string> is some user inputted value
// (officialName:<search_string>*^3 OR deliverableName:<search_string>*^3 OR alias:<search_string>*^3 OR contact:<search_string>*)
I have setup a design doc and index using the Cloudant dashboard as follows:
{
"_id": "_design/bySearchPP-ddoc",
"_rev": "4-a91fc4ddeccc998c58adb487a121c168",
"views": {},
"language": "javascript",
"indexes": {
"bySearchPP-indx": {
"analyzer": {
"name": "perfield",
"default": "standard",
"fields": {
"alias": "simple",
"contact": "simple",
"deploymentTarget": "keyword",
"businessUnit": "keyword",
"division": "keyword",
"officialName": "simple",
"deliverableName": "simple",
"pid": "keyword"
}
},
"index": "function(doc) {
if (doc.docType === \"Page\") {
index(\"officialName\", doc.officialName, {\"store\":true, \"boost\":4.0});
index(\"deliverableName\", doc.deliverableName, {\"store\":true, \"boost\":3.0});
if (doc.aliases) {
for (var i in doc.aliases) {
index(\"alias\", doc.aliases[i], {\"store\":true, \"boost\":2.0});
}
}
if (doc.allContacts) {
for (var j in doc.allContacts) {
index(\"contact\", doc.allContacts[j], {\"store\":true, \"boost\":0.5});
}
}
index(\"deploymentTarget\", doc.deploymentTarget, {\"store\":true});
index(\"businessUnit\", doc.businessUnit, {\"store\":true});
index(\"division\", doc.division, {\"store\":true});
index(\"pid\", doc.pid.toLowerCase(), {\"store\":true});
}
}"
}
}
}
I am not sure if the sort is working and just not working how I want it to or if I have misconfigured something. Either way, any help would be greatly appreciated. -Doug
Solved my own issue w/ help from comments above. Apparently everything was setup correctly but once I debug per #markwatsonatx I could see the field I wanted wasn't being returned. Did some digging online and apparently for sort the field must be both indexed and NOT tokenized. Thus I checked my index and noticed that the filed was being analyzed by the Simple analyzer. Changed it to the Keyword and the sort works as expected. Hoep this helps someone.

getting detail country name and country code of a given phone number

I able to detect incoming calls in android but problem is that I want to know to the country code of that phone number so that I can analyze whether it is a national or international number. I know about libphonenumber
but It needs to know region code before hand to get the country code as shown in the example
String swissNumberStr = "044 668 18 00";
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
try {
PhoneNumber swissNumberProto = phoneUtil.parse(swissNumberStr, "CH");
} catch (NumberParseException e) {
System.err.println("NumberParseException was thrown: " + e.toString());
}
it already knows to put "CH" as a parameter. but we don't know if the number is to known to us then how we can analyze that number.
As long as it is not a regional number (in which case it is just a plain number and no lib in this world could determine where it is from) you can specify "ZZ" or null for the second parameter of parse.
This is what the documentation says:
region that we are expecting the number to be from. This is only used
if the number being parsed is not written in international format. The
country_code for the number in this case would be stored as that of
the default region supplied. If the number is guaranteed to start with
a '+' followed by the country calling code, then "ZZ" or null can be
supplied.
After you where able to create an instace of PhoneNumber you can simply call getRegionCodeForNumber as shown below.
String swissNumberStr = "044 668 18 00";
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
try {
PhoneNumber numberProto = phoneUtil.parse(swissNumberStr, "ZZ");
System.out.println(phoneUtil.getRegionCodeForNumber(numberProto));
} catch (NumberParseException e) {
System.err.println("NumberParseException was thrown: " + e.toString());
}

Using rest-assured get value using path method

I want to extract value of s.d.url from the below JSON.
I am using the below satement as shown using System.out.println
but I dont get the result. How do I do it when the field itself contains "."
JSON
{
"data":{
"H1":{
"com.abc.def":{
"a_enabled":false,
"b_config":true
},
"c.s.urls":{
"s.d.url":"https://url1.com",
"w.p.url":"https://url2.com",
"s.c.url":"https://url3.com"
},
"com.abc.con":{
"e_n":true,
"a_r":false,
"c_t":"XYZMB"
}
},
"dCId":"ABCD"
}
}
ExtractableResponse<Response> spec = given()
.request().log().all()
.expect().statusCode(200)
.when()
.get(EndpointsCloudServices.getConfigUrl() + "?" + params)
.then().log().body()
.extract();
//want to get value of s.d.url
System.out.println("Triage???? " + spec.path("data.H1.c.s.urls.s.d.url"));
Give a try for the following, it will return value of s.d.url (pay attention for square brackets and single quotes):
spec.path("data.H1.['c.s.urls'].['s.d.url']")
or even shorter, if you're sure that s.d.url is an unique name across the whole json document:
spec.path("$.['s.d.url']")
And next, this is just to illustrate common case of referring field which contains dots in its name by using JSONPath expression - all you need is to wrap the field name in [' and ']
Example JSON:
{
"field.name": "value",
"nested": {
"field.with.dot": {
"field.inside": "anotherValue"
}
}
}
Example valid JSONPath expressions to access corresponding field values:
$['field.name']
$.['field.inside']
nested.['field.with.dot'].['field.inside']
Hint: you can quickly test your JSONPaths agains your json using tools like online evaluator or expression tester

How to bind an hstore[] value in Java

I'm trying to store people's telephone numbers and addresses in a database table. I would like to support multiple phone numbers and addresses and expect the format to be different in different countries. I've decided to use hstore to allow that flexibility and allow efficient querying by specific fields. As it stands I can receive the values from database, but could not find a way to insert them from Java. The table (simplified) looks like that:
CREATE TABLE IF NOT EXISTS contacts ( "
+ "id uuid NOT NULL, "
+ "title character varying NOT NULL DEFAULT '', "
+ "first_name character varying NOT NULL, "
+ "last_name character varying NOT NULL, "
+ "phones hstore[] NOT NULL DEFAULT '{}', "
+ "addresses hstore[] NOT NULL DEFAULT '{}')"
I have created a custom JDBI Binder to bind the values, but however I try I can't get the statement to execute. Currently Binder code snippet looks like this:
#Override
public void bind(SQLStatement<?> q, BindContactBean bind, ContactBean bean) {
q.bind("phones",
getHstoreArray(q, PhoneDetailMapper.toMapArray(bean.phones.get())));
q.bind("addresses",
getHstoreArray(q, AddressDetailMapper.toMapArray(bean.addresses.get())));
The getHstoreArray function is a helper that converts java Array into SQL array and looks like this:
private Array getHstoreArray(SQLStatement<?> q, Map<String, String>[] map) {
try {
return q.getContext().getConnection().createArrayOf("hstore", map);
} catch (SQLException e) {
throw new IllegalArgumentException(e);
}
}
I think the problem is in encoding of the data. For example, for data (in JSON notation for simplicity)
{
"firstName": "Maximum",
"lastName": "Details",
"status": "active",
"phones": [{
"type": "mobile",
"number": "0777 66 55 44"
}]
}
the query is expanded to:
INSERT INTO contacts (
id, first_name, last_name, status, phones )
VALUES ( '9be1a040-b408-11e3-bb43-00231832fa86', 'Maximum', 'Details', 4,
'{"{type=mobile, extracted_number=extracted, number=07777 66 55 44}"}'
)
and if I try to run it from PGAdmin's SQL editor the error returned is:
ERROR: Syntax error near 'm' at position 6
LINE 5: '{"{type=mobile, extracted_number=extracted, number=07777 66...
^
********** Error **********
ERROR: Syntax error near 'm' at position 6
SQL state: XX000
Character: 179
I have considered using JSON instead of hstore[], but that would make querying by specific fields slower and less accurate (essentially a text search) and I'd rather avoid it.
Another option I tried before hstore is array of UDT, but couldn't even get it to read from database without writing parser for PGobject which doesn't look like a simple task.
EDIT
I had a look at the data in the database and when escaped in the following way:
'{"\"type\"=>\"mobile\", \"number\"=>\"07777 66 55 44\", \"extracted_number\"=>\"777665544\""}'
I can run query manually from SQL editor, but still no luck in Java.
I have found a solution, there is a class available in Postgres driver called HStoreConverter which can convert Map to String literal. Not sure this is the best approach, but it seems to work, modified helper function below.
private Array getHstoreArray(SQLStatement<?> q, Map<String, String>[] maps) {
try {
String[] hstores = new String[maps.length];
for (int i = 0; i < maps.length; i++)
hstores[i] = HStoreConverter.toString(maps[i]);
return q.getContext().getConnection().createArrayOf("hstore", hstores);
} catch (SQLException e) {
throw new IllegalArgumentException(e);
}
}

Categories