Null check with vavr - java

For my API I'm parsing an object and creating my object with the lombok builder. One of the variables is an "arrivalTime" of type LocalDateTime that, of course, could be null.
I used to have it like this:
visits.add(visit.builder()
.arrivalTime(legacyVisit.getArrivalTime() == null ? null :
LocalDateTime.parse(legacyVisit.getArrivalTime(), ISO_OFFSET_DATE_TIME))
But I'm looking into a nicer way of doing this, maybe using a vavr option? But I'm running into problems. I understand that I put into "map" the logic if it's not null and into "get" the logic if it's null. But I can't do get(null). If arrivalTime is null, I want to keep it as null.
visits.add(visit.builder()
.arrivalTime(Option.of(legacyVisit.getArrivalTime())
.map(p -> LocalDateTime.parse(p, ISO_OFFSET_DATE_TIME)))
I also tried converting my arrivalTime variable in my object to an Option but I just send the object as a response in my API and it turns it into something like this:
"arrivalTime": {
"empty": true,
"lazy": false,
"singleValued": true,
"async": false,
"defined": false,
"orNull": null
},
which is very ugly. Any ideas?

It looks like having arrivalTime being an Option would be the most expressive and powerful. You can easily map the values as you mentioned.
But as mentioned, the serialization might need some work. There are some Vavr modules which do exactly that:
vavr-jackson which you could use when you're using jackson as JSON serializer library
vavr-gson which you could use when you're using GSON as JSON serializer library

Related

Validate facts having nested maps using drools

I have a class Fact which is a extends java.util.HashMap class. I am passing object of this class as fact to drools.
Now an instance of fact looks like this (Map<String, Object>):
{
"key1": "value"
"attributes": [{"name": "name1", "value": "value1"},{"name": "name2", "value": "value2"},{"name": "name3", "value": "value3"}...]
"locks": [{"type": "type1", "value": "value1", "attributes": {"key_a1": "val_a1""key_a2": "val_a2"...}}]
}
Running validations on root level entries in this map is straight forward e.g. running validations on key1.
Now, I want to run some validations on attributes and locks.
For attributes, I want to ensure that all attributes which are needed are present in this map and their corresponding values are correct. So I do this in the when block:
fact: Fact(this["key1"] != null && this.containsKey("attributes"));
attributesEntries: Entry(key == "attributes") from fact.entrySet();
attributesMaps: LinkedHashMap() from attributesEntries;
fact is HashMap
attributes are of type ArrayList<LinkedHashMap<String, String>> (an id key is also added for the LinkedHashMap whose value is the value of key name only).
locks are of type ArrayList<LinkedHashMap<String, Object>>
locks have attributes of type Map<String, String>
but it is not working. When I evaluate attributesEntries it is ArrayList<LinkedHashMap> and it has all the expected values but attributesMaps comes as empty. I also tried passing filters like LinkedHashMap(key == 'key1', value == 'val1') but that also didn't work. Tried looking for solutions and none were available for this sort of structure. Whatever was available I tried to extend but didn't work.
Is this possible to achieve and if so how? Also, how do I validate value (not empty and matches a pattern) once I am able to get it from the Map.
I am new to drools and we are using 5.4.0.Final version of drools.
Also, how can I work with the next level nested Map in locks.
I once had the misfortune of working on a project where we made this same mistake and had our class extend HashMap. (Fair warning: HashMap doesn't serialize well so you're going to use a lot of extra memory.)
I'm going to assume several things about your model because you neglected to share the class definition itself.
But I'm going to assume the following, based on your example JSON:
You have added a string value ("value") with the key "key1"
You have added a List<Map<String, ?>> value (possibly a List<Fact>) with the key "locks"
You have added a List<Map<String, ?>> value (possibly a List<Fact>) with the key "attributes"
The HashMap's get(key) method will return an object value; you've already noted the special this[ key ] syntax.
From your partial rule attempt, it's not entirely clear what you're trying to do. I think you're trying to get the List<Map<String, ?>> that is saved in your map under the "attributes" key.
rule "Do something with the attributes"
when
$fact: Fact( this["key1"] != null,
$attr: this["attributes"] != null )
then
System.out.println("Found " + $attr.size() + " attributes");
end
this["attributes"] returns the value associated with the key attributes. In this case, it's a List or whatever you shoved in there. If the key doesn't exist, the null check handles that.
You also asked how you could do stuff with a child map inside one of those lists. Let's say that want to do something with the attribute that has "name": "name1" ...
rule "Do something with the 'name = name1' attribute"
when
$fact: Fact( this["key1"] != null,
$attributes: this["attributes"] != null )
$nameAttr: Map( this["name"] == "name1" ) from $attributes
then
// do something with $nameAttr
end
The pattern repeats, of course. Let's say you've shoved yet another List<Map<String, ?>> into your attribute maps:
rule "Do something with a child of 'name' attribute"
when
$fact: Fact( this["key1"] != null,
$attributes: this["attributes"] != null )
$nameAttr: Map( this["name"] == "name1",
$attrKids: this["children"] != null ) from $attributes
$childNameAttr: Map( this["name"] == "child1" ) from $attrKids
then
// etc.
end
I strongly recommend reconsidering your object model to not be Map-based. At the company I worked at where all of our projects were built against a nested Map-based model and running Drools 5.0.1, I spent significant time and effort upgrading parts of it to Drools 7 and a proper model that passed in just the data we needed. It saved a ton of resources and ended up being much faster.

How to remove the json key value with JSonPath in java

I know it could be a duplicate, but still posting my question as i could not find the exact answer what i am looking for. I am having an json object (or string) like below.
String str = "{
"status" : {
"timestamp" : "2020-04-30T01:00:00 000Z"
"error" : 0,
"error_message" : null,
"execution" : "completed"
}
}
";
I will get the a same kind of response from my REST API testing, but after each call the 'timestamp' key will be having a dynamic date and time value with the time respect to the call made. And here i compare my expect json with the actual json as a whole sting comparison using JSONAssert. As the timestamp value is different it always fails for me.
So my question is before i do any comparison, i would like to remove the 'timestamp' key and its value from json to compare and so it will pass my case. I tried by using JsonPath also, but did not works. Any help on this please?
JSONAssert allow you to make a customized comparator while doing asserts [1].
In your case it's easy as:
JSONAssert.assertEquals(expectedJson,
actualJson,
new CustomComparator(JSONCompareMode.LENIENT,
skips("status.timestamp","another.json.path", ...)));
private static Customization[] skips(String... jsonPaths) {
return Arrays.stream(jsonPaths)
.map(jsonPath -> Customization.customization(jsonPath, (o1, o2) -> true))
.toArray(Customization[]::new);
}
Here we are defining CustomComparator, with customization which takes JSONPath (status.timestamp) and takes a ValueMatcher (lambda) which compares two values for that specific JSONPath.
In our case we will always return true, which will effectively skips the value (no matter with what we are comparing that value it's always true).
Edit: As you can see CustomComparator's constructor takes varargs of Customizations, hence you can provide more than one field to be ignore from comparison.
[1] http://jsonassert.skyscreamer.org/apidocs/org/skyscreamer/jsonassert/Customization.html
[2] http://jsonassert.skyscreamer.org/apidocs/org/skyscreamer/jsonassert/comparator/CustomComparator.html

mongodb find and return id as a string - java

Morning,
Seem to be having a brainfart! Have had a look around and can't see any one else having the same issue so I've either completely missed something (most likely) or nobody else has this use case.
I basically want to return all objects stored in a mongodb collection, including their id's, however as the string representation rather than the full object. So this:
public ArrayList findAllObjects(){
return db.getCollection("objects").find().into(new ArrayList<Document>());
}
{
_id: {
class: "org.bson.types.ObjectId",
counter: 7230903,
date: "2016-10-03T12:39:38Z",
machineIdentifier: 5652488,
processIdentifier: 8859,
time: 1475498378000,
timeSecond: 1475498378,
timestamp: 1475498378
},
name: "Test Object"
},
Now if I run a find on the mongo console I get something along the lines of:
{
"_id": ObjectId("57f2518a564008229b6e55b7"),
"name": "Test Object"
}
It's this 57f2518a564008229b6e55b7 that I'd like to return in the original json as the _id (potentially could add under another name) field.
I can get that string representation in the java code simply by running get getObjectId() on an individual document. So I could possibly loop through every result and set/add the _id but that feels like a bit of a smell to me.
Any suggestions would be welcome.
Thanks
Update:
Thanks Sinclair for the comments, I don't believe this is a duplicate though, as I do actually want to include the id not necessarily exclude anything. If the string representation was in the org.bson.types.ObjectId object as a property I could then potentially exclude the rest but that isn't the case.
You can convert the object to string simply using the toString() method:
List<Document> documents = collection.find().into(new ArrayList<>());
documents.parallelStream().forEach(document -> {
document.put("_id", document.get("_id").toString());
});

JSON Object can't be converted to JSON Array

ya i know that it's very usual problem while mapping but my problem is some different hear is the scenario
when my response have the data it gives me JSON Response like this
{
"responseID": "110",
"resultSet": [
{
"USERNAME": "Aninja",
"position": "Developer",
"salary": "60000"
}
],
"isSuccessful": true,
"rtnCode": "0000"
}
and below is the same JSON response when data is not found
{
"responseID": "123",
"resultSet": {},
"isSuccessful": true,
"rtnCode": " "
}
as i can see hear when response have some data result set have JSON Array but when no data found we have JSON Object as a response
so this is the reason I'm getting this problem.
so my question is that how should i handle this problem thanks for your response
Edit: the main problem is that i have made my model like list of JSON Object it works fine when there is result but it gives me error Can't convert JSON Object to JSON Array when result is empty s please suggest me how can i hanle it I'm using Jackson 2.2 i have also tried #JsonInclude(Include.NON_EMPTY) and #JsonInclude(Include.NON_NULL)
I wouldn't say it is mistake from server or back-end. But it is always a good practice to provide appropriate "Null Object Pattern" which describes the uses of such objects and their behavior.
So for better practice array which doesn't have any values should be sent back using "[]". So in this case "resultSet" should be given as [] instead of {} so it can be easily understood at front-end.
There are number of examples here which shows why it is useful to follow Null Object Pattern.
For example, if you are returning count in you response and there is no count then it is better to use "0" instead of "null".

Anyway to overwrite parts of a json file in java?

I need to be able to parse sql calls from a database to json and then compare the key fields in the parsed JSON string against a json file (it's technically a BOD --Business Object Document) and if they match then I need to overwrite the json file's matching value with that of the JSON string.
e.g. I parse the sql call to this
{
"partyInfo": {
"PARTY_NAME": "NORWAY",
"STATE": "OSLO",
"PARTY_ID": "92706031",
"VERTICAL_MARKET_TOP_DESC": null,
"ATTRIBUTE20": null,
"DUNS_NUMBER": null,
"SIC_CODE": null,
"EMPLOYEES_TOTAL": null,
"ALL_ADDRESS_LINES": "HOMMENKOLLEN 23 TOPPEN 12",
"CITY": "OSLO",
"POSTAL_CODE": "1255",
"COUNTRY_NAME": "NORWAY",
"KNOWN_AS": null
}
}
and then compare it against a file that looks like this:
{
"partyInfo": {
"PARTY_NAME": string,
"STATE": string,
"PARTY_ID": number,
"SIC_CODE": string,
}
}
and overwriting values on matching keys s.t that the ending file looks like this:
{
"partyInfo": {
"PARTY_NAME": "NORWAY",
"STATE": "OSLO",
"PARTY_ID": "92706031",
"SIC_CODE": null,
}
}
So far I've been able to parse the SQL calls to JSON (using Jackson right now but I'm willing to change if need be) but I don't know how to compare against the file and overwrite only the data values that match up.
It looks like the tree model allows you to update nodes.
Here's an example:
http://wiki.fasterxml.com/JacksonInFiveMinutes#Tree_Model_Example
I may be misunderstanding what you're wanting to do, but it sounds like you could just use a for loop over the keys in the Map representing the JSON from the file, compare the values to those in the database, change any that don't match, and then write the file back out:
for(String key: fileJson.keys())
if(!fileJson.get(key).equals(sqlJson.get(key)))
fileJson.put(key, sqlJson.get(key));
// write fileJson back out to the correct file through Jackson
The simplest solution would be to parse these files into java objects (using Jackson again), compare the objects and then save what you need to save.
Otherwise you'll effectively be making something like the patch tool.

Categories