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
Related
target = ObjectUtils.defaultIfNull(getCustomerByAddressID(sourceData, supportParam),
createNewCustomer(supportParam));
I am passing 2 functions to ObjectUtils.defaultIfNull. First is function which returns Customer if found by AddressID else null, second param is function which create new Customer.
After execution I am seeing 2 Customers, debug is showing even after getCustomerByAddressID(sourceData, supportParam) returns not null value - createNewCustomer(supportParam) is getting executed.
Is this issue because of Code formatting or what am I missing? Should I use Optional.ofNullable().orElse() instead of ObjectUtils.defaultIfNull?
You can use Optional.ofNullable(getCustomerByAddressID(sourceData, supportParam)).orElseGet(() -> createNewCustomer(supportParam)) if you don't want createNewCustomer(supportParam) to be invoked, even if getCustomerByAddressID(sourceData, supportParam) is null
You should use
target = Optional.ofNullable(getCustomerByAddressID(sourceData, supportParam))
.orElseGet(() -> createNewCustomer(supportParam));
Because both Optional.ofNullable().orElse() and ObjectUtils.defaultIfNull() which uses ternary operator internally, always call "orElse" part.
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
I am using jayway JsonPath, Suppose I have Json like this:
jsonObject:
{
"balance": 50000000000000.0
}
jspath: $.balance
val response =JsonPath.parse(jsonObject).read(jsPath).toString
println("response: ", response )
here I am getting value as "response: 5.0E7" but I want it as "50000000000000.0"
I know how to convert from double to string. But my case is I have to call external Rest API and I will get json as response, based on jspath it has to fetch original value. It means the same code has to work for string, integer and double types with its original content, but not to its specific content format.
{
"balance": "50000000000000.0" (here balance is string)
}
expected: 50000000000000.0 result: 50000000000000.0
{
"balance": 50000000000000.00 (here balance is double)
}
expected: 50000000000000.0 result: 5.0E7(but I need 50000000000000.0)
The same code it has to work for both scenarios or is there any other library to get this solution or is there any chance of getting return type from read(), so using exception handling I can handle that.
Since you use Scala you can simply read value as Any and format it using pattern matching, e.g.:
val response: Any = JsonPath.parse(jsonObject).read(jsPath)
val formattedResponse = response match {
case d: Double => d.formatted("%f")
case _ => response.toString
}
Alternatively you can configure Jayway JsonPath to always parse floating numbers as BigDecimal, whose toString in most cases will print value without exponents (unless it's really small fraction (~ <1e-6), which anyway would be more readable with exponent) and has additional advantage of being capable of storing longer numbers without losing precision. This would however require configuring JsonPath with JsonProvider that always uses BigDecimal (default SmartJson can't do that, Jackson will work fine).
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());
});
So I have been using jayway JSONPath to query JSON Objects much like the following:
{
"messageHeader" : {
"sentBy" : "someOne",
"userName" : "John Doe"
},
"payload" : []
}
And this is working fine for the most part, except now I wish to select the root level objects using the path $..* and preform separate tasks depending on the type of object present in the message, using their key/names as an identifier.
However, using said path, or $.* , will always produces a JSONArray much like this:
[{sentBy:someOne,userName:John Doe},[]]
The JSON objects appear to be anonymous, they have no keys. Is there anyway I can access the key for these objects directly as a String? If the data is not present, then why does the path: $.messageHeader.sentBy , work?
From README of the JsonPath :
When evaluating a path you need to understand the concept of when a
path is definite. A path is indefinite if it contains:
.. - a deep scan operator
?() - an expression
[, (, )] - multiple array indexes
Indefinite paths always returns a list (as represented by current JsonProvider).
This should explain the above phenomenon.