Error in JSON array comparison customization(sky-screamer) - json

I have below two JSONs to compare,
expected json:
[
{
"id": 1,
"code": 1,
"createdOn": null,
"lastModifiedOn": null
},
{
"id": 2,
"code": 1,
"createdOn": null,
"lastModifiedOn": null
}
]
actual json
[
{
"id": 1,
"code": 1,
"createdOn": "2019-12-31",
"lastModifiedOn": "2019-12-31",
},
{
"id": 2,
"code": 1,
"createdOn": "2019-12-31",
"lastModifiedOn": "2019-12-31",
}
]
Trying to compare by ignoring couple of nodes using below code
JSONAssert.assertEquals(actualjson, expectedjson,
new CustomComparator(JSONCompareMode.STRICT,
new Customization("createdOn", (o1, o2) -> {
return true;
}),
new Customization("lastModifiedOn", (o1, o2) -> {
return true;
})
)));
but it is failing with below assertion exception,
java.lang.AssertionError: [0].createdOn
Expected: null
got: 2019-12-31
; [0].lastModifiedOn
Expected: null
got: 2019-12-31
; [1].createdOn
Expected: null
got: 2019-12-31
; [1].lastModifiedOn
Expected: null
got: 2019-12-31
how can I compare array of json values with customization object by skipping createdon and lastmodifiedon nodes?
<groupId>org.assertj</groupId>
<version>2.2.1</version>

Yes below code snippet
JSONAssert.assertEquals(actualjson, expectedjson,
new CustomComparator(JSONCompareMode.STRICT,
new Customization("**.createdOn", (o1, o2) -> true),
new Customization("**.lastModifiedOn", (o1, o2) -> true)
)));
Eventually Customization.getCustomization method invoke appliesToPath method , which invoke this.path.matcher(path).matches();
matcher method is from java.util.regex , so if your pattern "**.createdOn" matches with path "[0].createdOn" , "[1].createdOn" then your Customisation will be added CustomComparator which inturn call your method "(o1, o2) -> true"

Approach 1: Parse the JSON and recreate the two object JSONs without the date properties.
Approach 2: Parse the JSON and put an assertion on each property to be compared.

Give a try to JsonUnit it allows you to ignore values, elements or path which should help.

Since SkyScreamer has open issue noted in github I found temporary solution and thought would be helpful for others,
https://github.com/skyscreamer/JSONassert/issues/109
solution:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
List<DomainObject> actualDomainObj = mapper.readValue(actualJson, new TypeReference<List<DomainObject>>() {
});
List<DomainObject> expectedDomainObj = mapper.readValue(expectedJson, new TypeReference<List<DomainObject>>() {
});
assertDomainObjResults(actualDomainObj.get(0), expectedDomainObj.get(0));
private void assertDomainObjResults(DomainObject actual, DomainObject expected) {
softAssertions.assertThat(actual.getId()).isEqualTo(expected.getId());
softAssertions.assertThat(actual.getLastModifiedOn()).isEqualTo(LocalDate.now());
softAssertions.assertThat(actual.getCreatedOn()).isEqualTo(LocalDate.now());
}
accept the answer if someone finds it useful.

I recently created a custom comparator that lets you use regular expressions in the 'expected' JSON:
public class ExtendedJsonComparator extends DefaultComparator {
public ExtendedJsonComparator(JSONCompareMode mode) {
super(mode);
}
#Override
public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException {
String expected = expectedValue.toString().trim();
String actual = actualValue.toString();
if(expected.startsWith("${") && expected.endsWith("}")) {
String regex = expected.substring(2, expected.length() - 1);
if(!actual.matches(regex)) {
result.fail(prefix, expected, actual);
}
} else {
super.compareValues(prefix, expectedValue, actualValue, result);
}
}
}
For 'expected, you can then do the following, or create a regex to match your date format, if that's something you're testing:
[
{
"id": 1,
"code": 1,
"createdOn": "${.*}",
"lastModifiedOn": "${.*}"
},
{
"id": 2,
"code": 1,
"createdOn": "${.*}",
"lastModifiedOn": "${.*}"
}
]

Related

Assert key name in the json response using groovy

I want to assert a key in the json response.
Response
{
"availableRooms": [
{
"roomId": 2,
"exchangeEmailId": null,
"roomName": "Room 1",
"floor": 0,
"groupID": 3,
"defaultCapacity": 8,
"maxCapacity": 8,
"attributes": [
{
"attributeID": 170,
"displayName": "Video Conference Unit",
"abbreviation": "VCU"
}
],
"externalRoomEmail": "vinay.jaiswal#condecosoftware.com"
}
],
"availableRoomsForAllDates": {},
"callResponse": {
"message": null,
"customResponseCode": null
}
}
Groovy
import groovy.json.JsonSlurper
def ResponseMessage = messageExchange.response.responseContent
def jsonSlurper = new JsonSlurper().parseText(ResponseMessage)
if( jsonSlurper.containsKey( 'externalRoomEmail' ) )
{
log.info 'a'
}
else
{
log.info 'b'
}
Output
Tue Nov 24 20:24:35 IST 2020:INFO:b
Is there any inbuilt method?
If I try jsonSlurper.availableRooms.externalRoomEmail then it gives me null but testcase is passed.
I want that it should break the testcase if key not found.
<edit - just re-read your question, answer adjusted accordingly>
First of all I'm going to call your parsed json json instead of jsonSlurper as the slurper is the groovy class used to parse json and it's conceptually confusing to name the data "slurper".
So your problem is that json.availableRooms returns a list. In other words you would have to use something like:
if(json.availableRooms.first().externalRoomEmail) {
...
} else {
...
}
to check if the first room had an externalRoomEmail defined.
From there is depends on what you want to do. Let's say that you wanted to see if any of the available rooms had an externalRoomEmail defined, you would then do something like:
def roomsWithEmails = json.availableRooms.findAll { it.externalRoomEmail }
if (roomsWithEmails) {
roomsWithEmails.each { room ->
println "room ${room.roomName} has external email ${room.externalRoomEmail}"
}
} else { // no rooms with externalRoomEmail
// do something else
}

How to add custom properties to Laravel paginate json response

I have the following simple index method:
public function index()
{
// dd(Poll::paginate(2));
return response()->json(Poll::paginate(2),200);
}
The output of that method is looks like the following json object:
{
"current_page": 1,
"data": [
{
"id": 1,
"title": "I suppose?' said Alice. 'Why, you don't know.",
"created_at": "2018-09-14 16:42:11",
"updated_at": "2018-09-14 16:42:11"
},
{
"id": 2,
"title": "Alice; but she knew that it seemed quite.",
"created_at": "2018-09-14 16:42:11",
"updated_at": "2018-09-14 16:42:11"
}
],
"first_page_url": "http://127.0.0.1:8000/polls?page=1",
"from": 1,
"last_page": 6,
"last_page_url": "http://127.0.0.1:8000/polls?page=6",
"next_page_url": "http://127.0.0.1:8000/polls?page=2",
"path": "http://127.0.0.1:8000/polls",
"per_page": 2,
"prev_page_url": null,
"to": 2,
"total": 11
}
I want to add another array property after "total: 11" attribute such as:
,
"anotherData" : [
"userId": 1,
"userName": "john10",
"message": "This is a message"
]
I have tried to understand how response()->json() works, so it can extract some data from LengthAwarePaginator object which it is the output of Poll::paginate(2) from this resource, but I could not able to understand how does it able to get an array from LengthAwarePaginator that holds the resultant keys in the json object?!
From the regarded resource above, the json() method takes an array and, I guess that, if the parameter is not an array, it tries to convert it to array, specially if it is an object like LengthAwarePaginator, so it may use toArray() method.
I have tried replacing return response()->json(Poll::paginate(2),200) with return response()->json(Poll::paginate(2)->toArray,200), then I have got the same output. Hence, I have decided to replace the code of my index method to be like the following:
public function index()
{
//dd(Poll::paginate(2)->toArray());
$output = Poll::paginate(2)->toArray();
$output['userData'] = ['userId' => \Auth::user()->id, 'userEmail' => \Auth::user()->email];
return response()->json($output,200);
}
The resultant output is:
...
"path": "http://127.0.0.1:8000/polls",
"per_page": 2,
"prev_page_url": null,
"to": 2,
"total": 11,
"userData": {
"userId": 1,
"userEmail": "lion#example.com"
}
}
If you're directly responding with the paginated data, a better option would be to wrap it in a Resource Collection and adding meta data to it.
This is how I implemented it.
CategoryController.php
public function index()
{
return new CategoryCollection(Category::paginate(10));
}
CategoryCollection.php
public function toArray($request)
{
return [
'status' => 1,
'message' => 'Success',
'data' => $this->collection,
];
}
Since Collection extends JsonResource, your response would automatically be converted to JSON when returned from the Controller.

Want to remove Unexpected object like "headers","original","exception" from Laravel JSON output

I am getting JSON response like this. But I want to remove "headers", "original" and "exception".
{
"headers": {},
"original": [
{
"id": 271,
"name": "TestController",
"parent_id": null
}
],
"exception": null
}
Output expected:
{
"data": {
"id": 271,
"name": "TestController",
"parent_id": null
},
"errors": [],
"success": true,
"status_code": 200
}
You are returning a response()->json() inside another response()->json() something along the way:
response()->json(response()->json($data,200),200)
or more like:
$data = [
"id"=> 271,
"name"=> "TestController",
"parent_id"=> null
];
$response = response()->json($data,200);
return response()->json($response ,200);
You may not have notice it because of a function returning the first response()->json() into the second one
You can use this
$json='{
"headers": {},
"original": [
{
"id": 271,
"name": "TestController",
"parent_id": null
}
],
"exception": null
}';
$arr=json_decode($json);
$data=$arr->original[0];
$new_json=array();
$new_json['data']=$data;
$new_json['errors']=[];
$new_json['success']=true;
$new_json['status_code']=200;
$new_json=json_encode($new_json);
You may have doubled the data json return with response()->json()
you can use array only
return ["data"=> [
"id"=> 271,
"name"=> "TestController",
"parent_id"=> null
],
"errors"=> [],
"success"=> true,
"status_code"=> 200
];
In my case this problem solved with this solution:
You can use:
return json_decode(json_encode($ResponseData), true);
And return response
This is what I did, and it worked for me:
just call the original object after getting your response like this:
public function user_object(){
return $this->me()->original;
}
This is the me() function that returns user details
public function me()
{
return response()->json(auth('users')->user()->only(['id','name','email','status','phonenumber','type']));
}
This is my response from post man:
{
"success": true,
"user": {
"id": 29,
"name": "test6",
"email": "test6#gmail.com",
"status": 1,
"phonenumber": "413678675",
"type": "user"
},
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC8xMjcuMC4wLjE6ODAwMFwvYXBpXC9hdXRoXC9yZWdpc3RlciIsImlhdCI6MTU5OTQ3MDc3OCwiZXhwIjoxNTk5NDc0Mzc4LCJuYmYiOjE1OTk0NzA3NzgsImp0aSI6InFyUWEyTVNLVzR4a2o0ZVgiLCJzdWIiOjI5LCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.SMHgYkz4B4BSn-fvUqJGfsgqHc_r0kMDqK1-y9-wLZI",
"expires_in": 3600
}
The issue is triggered because you are returning nested responses somewhere in your code.
Here is a simple code that demonstrates the issue and the fix.
// A normal function that you think it returns an array
// but instead, it is returning a response object!
public function get_data(){
//ISSUE
return response([1, 2, 3]); // <- this will trigger the issue becuase
// it returns the data as a response not an array
//FIX
return [1, 2, 3]; // <- this will work as intended
// bacause the data is returned as a normal array
}
public function get_all_data(){
$first_array = [1, 2];
$second_array = [2, 3];
$third_array = get_data(); // <- here is the call to the function
// that should return an array
//Return the JSON response
return response([first_array, second_array, third_array]);
}

How to remove Task json properties in Nancy.Response.AsJson

I've made one of my API endpoints and inner logic asynchronous and when previously I've used Response.AsJson(Foo.bar()) , it would return the json representation normally, but now I see this appended to it:
{
"result": [
{
"id": "59d680cc734d1d08b4e6c89c",
"properties": {
"name": "value"
}
}
],
"id": 3,
"exception": null,
"status": 5,
"isCanceled": false,
"isCompleted": true,
"isCompletedSuccessfully": true,
"creationOptions": 0,
"asyncState": null,
"isFaulted": false
}
But I want it to be like this:
"id": "59d680cc734d1d08b4e6c89c",
"properties": {
"name": "value"
}
As I understand, it's because I've wrapped my object in a Task , but I can't figure out, how with Nancy framework, which I use the Response.AsJson, to make it so the properties are excluded. I can obviously omit the Response.AsJson of the returned object, but then response is no longer Json if requesting through web-browser for example.
For further example
NancyModule for routing API:
public ItemCatalogModule(IItemCatalog itemCatalog) : base("/itemCatalog")
{
Get("/fetch/{id}", async parameters =>
{
var id = (string) parameters.id;
var response = await Response.AsJson(itemCatalog.GetItem(id));
return response;
});
}
How the interface looks like of ItemCatalog:
public interface IItemCatalog
{
Task<Item> GetItem(string id);
}
You shoud do this :
public ItemCatalogModule(IItemCatalog itemCatalog) : base("/itemCatalog")
{
Get("/fetch/{id}", async parameters =>
{
var id = (string) parameters.id;
return Response.AsJson(await itemCatalog.GetItem(id));
});
}

Transform JSON to an appropriate format for RESTAdapter EmberJS

I receive a JSON from our API that has the following format
[
{
"id": 45,
"name": "Pasta",
"_order": 0,
"is_hidden": null,
"is_list": false
},
{
"id": 46,
"name": "Salads",
"_order": 1,
"is_hidden": null,
"is_list": false
},
{
"id": 47,
"name": "Dessert",
"_order": 2,
"is_hidden": null,
"is_list": false
}
];
I see that it has invalid format for standard RESTAdapter and I need to put the name of the model first. In my example it should probably be like:
{
"category":
[
{
"id": 45,
"name": "Pasta",
"_order": 0,
"is_hidden": null,
"is_list": false
},
{
"id": 46,
"name": "Salads",
"_order": 1,
"is_hidden": null,
"is_list": false
},
{
"id": 47,
"name": "Dessert",
"_order": 2,
"is_hidden": null,
"is_list": false
}
]
}
So how to make it look this way in my adapter? It seems like I should use DS.RESTSerializer, but I can't figure out which method I should override...
I ran into this issue earlier today. A nice clean way to fix it is to define a
normalizePayload method for your ApplicationSerializer. It's made to be overwritten, so you aren't affecting anything else.
E.g.
App.ApplicationSerializer = DS.RESTSerializer.extend({
normalizePayload: function(type, payload) {
return { category: payload };
}
}
If you want to do this on only some of the payloads processed then you just add a conditional inside it.
App.ApplicationSerializer = DS.RESTSerializer.extend({
normalizePayload: function(type, payload) {
if (type.toString() === 'App.Category') {
return { category: payload };
}
}
}
For more info on the normalizePayload method see http://emberjs.com/api/data/classes/DS.RESTSerializer.html#method_normalizePayload
In general, you'll probably want to implement a couple of methods in your serializer if you have to tweak your JSON:
App.ApplicationSerializer = DS.RESTSerializer.extend({
normalize: function(type, hash, prop) {
// change your incoming JSON to ember-style here
},
serialize: function(record, options) {
// change your outgoing ember-data record to your server's JSON here
}
});
EDIT:
You may also in your case need to override extractArray as well: http://emberjs.com/api/data/classes/DS.RESTSerializer.html#method_extractArray
Neither normalizePayload nor normalize is working for me. What I am doing is:
// app/serializers/application.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
extractArray: function(store, type, payload) {
var payloadTemp = {}
payloadTemp[type.typeKey] = payload;
return this._super(store, type, payloadTemp);
},
extractSingle: function(store, type, payload, id) {
var payloadTemp = {}
payloadTemp[type.typeKey] = [payload];
return this._super(store, type, payloadTemp, id);
}
});
Just nest the object represented by your JSON string inside a new object which adds the new property needed and then convert back to JSON. Like this:
var yourInitialJSONString; // your JSON from API
var obj = JSON.parse(yourInitialJSONString);
var newObj = {
category: obj
};
var finalJSON = JSON.stringify(newObj);
Alternatively, though probably only best for simple case and not as universally useful as working with actual objects, you could simply concatenate to your JSON:
var yourInitialJSONString; // your JSON from API
var finalJSON = '{"category":' + yourInitialJSONString + '}';
These might prove faster for simple use cases as you avoid deserialization and serialization. I just don't like it as much as I would rather work with actual objects represented by the data. If you need to do a more complex transformation it would probably prove to be more understandable in real word terms than doing a bunch of string concatenation/manipulation.