How to hide json field when using Springdata pageable? - spring

Currently I'm using SpringData to build my restful project.
I'm using Page findAll(Pageable pageable, X condition, String... columns); ,this method .The result looks like this:
{
"content": [
{
"id": 2,
"ouId": 1,
"pClassId": 3,
"isPublic": 0,
"accessMethod": 3,
"modifierName": null
}
],
"last": true,
"totalPages": 1,
"totalElements": 3,
"number": 0,
"size": 10,
"sort": [
{
"direction": "DESC",
"property": "id",
"ignoreCase": false,
"nullHandling": "NATIVE",
"ascending": false,
"descending": true
}
],
"first": true,
"numberOfElements": 3
}
The question is how to hide some specific json field in the content ?
And #JsonIgnore annotation is not flexible , the fields I need in different APIs are different.
I tried to write an annotation ,but when processing the Page ,I found that the content is unmodifiable .
So , hope that someone could help me with it.

If you don't want to put annotations on your Pojos you can also use Genson.
Here is how you can exclude a field with it without any annotations (you can also use annotations if you want, but you have the choice).
Genson genson = new Genson.Builder().exclude("securityCode", User.class).create();
// and then
String json = genson.serialize(user);
OR using flexjson
import flexjson.JSONDeserializer;
import flexjson.JSONSerializer;
import flexjson.transformer.DateTransformer;
public String toJson(User entity) {
return new JSONSerializer().transform(new DateTransformer("MM/dd/yyyy HH:mm:ss"), java.util.Date.class)
.include("wantedField1","wantedField2")
.exclude("unwantedField1").serialize(entity);
}

You have to use a custom serialize like the following:
#JsonComponent
public class MovieSerializer extends JsonSerializer<Movie> {
#Override
public void serialize(Movie movie, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
// The basic information of a movie
jsonGenerator.writeNumberField("id", movie.getId());
jsonGenerator.writeStringField("name", movie.getName());
jsonGenerator.writeStringField("poster", movie.getPoster());
jsonGenerator.writeObjectField("releaseDate", movie.getReleaseDate());
jsonGenerator.writeObjectField("runtime", movie.getRuntime());
jsonGenerator.writeStringField("storyline", movie.getStoryline());
jsonGenerator.writeStringField("rated", movie.getRated());
jsonGenerator.writeNumberField("rating", movie.getRating());
jsonGenerator.writeEndObject();
}
}
And then annotate your model class with: #JsonSerialize(using = MovieSerializer.class)

Related

How can I make jackson to use different views on nested entities of the same type?

Before I start questioning let me give you a simplified example of my case:
Imagine you have Views:
public final class Views {
public interface Id { }
public interface IdText extends Id { }
public interface FullProfile extends IdText { }
}
You also have a class User which has subscribers that are of the same type User.
The properties id and username are serialized in the Views.IdText.class view. And the property subscribers is serialized in the Views.FullProfile.class view.
#Entity
public class User implements UserDetails {
#Id
#JsonView(Views.IdText.class)
private Long id;
#JsonView(Views.IdText.class)
private String username;
#JsonIdentityReference
#JsonIdentityInfo(
property = "id",
generator = ObjectIdGenerators.PropertyGenerator.class
)
#JsonView(Views.FullProfile.class)
private Set<User> subscribers = new HashSet<>();
}
And a controller (ProfileController) that has a method called get that returns a user's profile.
#RestController
public class ProfileController {
#GetMapping("{id}")
#JsonView(Views.FullProfile.class)
public User get(#PathVariable("id") User user) {
// ... some service methods that has the necessary logic.
return user;
}
}
As you can see, the method serializes the user's profile in the Views.FullProfile.class view, so the output is:
{
"id": 39,
"username": "ryan",
"subscribers": [
{
"id": 42,
"username": "elliott",
"subscribers": [
{
"id": 432,
"username": "oliver",
"subscribers": [
{
"id": 2525,
"username": "james",
"subscribers": [
39,
432
]
},
{
// ... a lot of useless data goes on.
}
]
},
{
"id": 94923,
"username": "lucas",
"subscribers": [
345, 42
]
}
]
},
{
"id": 345,
"username": "jocko",
"subscribers": [
94923
]
}
]
}
When a user's profile is being serialized, I don't need the user's subscribers to be serialized in the Views.FullProfile.class view
but in the Views.IdText.class view so that the output would be:
{
"id": 39,
"username": "ryan",
"subscriptions": [
{
"id": 42,
"username": "elliott"
},
{
"id": 345,
"username": "jocko"
}
]
}
How can I make jackson to use different views on nested entities of the same type?
Or what else do I have to do to make that happen?
After some time of continuous searching I found someone issued the same problem on Github: #JsonIgnoreProperties should support nested properties #2940
As stated in that issue:
No plans to ever implement this (due to delegating design, will not be possible with current Jackson architecture), closing.

#GetMapping UTF-8 encoding not working properly - Croatian

I'm having problems with getting response in correct encoding, And I've tried everything I found on net.
MySQL database is in utf8mb4_croatian_ci collation.
Example of database records:
Abadžić Stanko
Ač Nataša
Adamović Dubravko
Adrić Marija
Here is my controller:
This is my rest controller:
#RestController
public class AutorController {
#Autowired
private AutorService autorService;
#GetMapping(value = "/autori", produces = "application/json;charset=UTF-8")
public List<Autor> getAllAutors() {
return autorService.getAllAutors();
}
}
application.properties where I tried to configure encoding for tomcat:
server.tomcat.uri-encoding = UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
spring.datasource.sql-script-encoding=UTF-8
spring.messages.encoding=UTF-8
spring.datasource.tomcat.connection-properties=useUnicode=true;characterEncoding=utf-8;
This is the response I get:
"id": 2,
"imeAutora": "Abadžić Stanko"
},
{
"id": 3,
"imeAutora": "A� Nataša"
},
{
"id": 4,
"imeAutora": "Adamović Dubravko"
},
{
"id": 5,
"imeAutora": "Adrić Marija"
}
P.S. Also when I change encoding in controller into ISO-8859-2 (also should support Croatian language):
#GetMapping(value = "/autori", produces = "application/json;charset=ISO-8859-2")
my response is like:
"id": 2,
"imeAutora": "AbadŞiÄ‥ Stanko"
which means that setting charset in #GetMapping is doing something , but it's doing it wrong
If you have any ideas, please let me know.

How to remove unwanted keys from rest-assured response object and assert remaining object data with constant variable having json string using java

In rest-assured test cases I am getting response as mentioned, where I want to remove keys such as "updated_at", "deleted_at", "created_at" and "notice" and then assert this response object with expected json string constant which contains 'settings'
{
"notice": "The Settings are updated successfully.",
"settings": {
"push_notification": {
"enabled": true,
"credentials": [{
"key": "value"
}],
"service_name": "API Testing"
},
"created_at": "2019-05-04T14:52:32.773Z",
"deleted_at": "false",
"updated_at": "2019-05-07T11:23:22.781Z"
}
}
For given response the expected json string is...
public static String SETTING_EXPECTED = "{\"push_notification\": {\"enabled\": true, \"credentials\": [{\"key\": \"value\"}], \"service_name\": \"API Testing\"}}"
Please help me with creating a common method using java which can be reuse for response assertions in all the test cases.
To delete keys from response you can use below code I am using jayway jsonpath library, you need to pass Json Response and field name jsonPath, in case your it will be "$.settings.created_at" :
public String deleteFieldNameFromResponse(String jsonResponse, String fieldToDelete)
throws ParseException, FileNotFoundException, IOException {
Object obj = null;
JSONParser parser = new JSONParser();
JsonPath jsonPath = null;
DocumentContext docCtx = null;
obj = parser.parse(jsonResponse);
docCtx = JsonPath.parse(obj);
docCtx.delete(fieldToDelete);
jsonPath = JsonPath.compile("$");
return docCtx.read(jsonPath).toString();
}

Spring ModelAndView to RestController json response

I have a legacy spring code where they use ModelAndView and they add the objects to it as below.
ModelAndView result = new ModelAndView();
result.addObject("folders", folders);
return result;
for the above i am getting response as
{
"folders": [
{
"recordCount": 0,
"folderContentType": "Reports",
"folderId": 34,
},
{
"recordCount": 2,
"folderContentType": "SharedReports",
"folderId": 88,
}
]
}
I have changed these to use Spring's RestController with a POJO backing the results returned from DB.
#GetMapping("/folders")
public List<Folder> getAllFolders() {
return Service.findAllFolders(1,2);
}
This returns a JSON as below
[
{
"folderId": 359056,
"folderName": "BE Shared Report Inbox",
"folderDescription": "BE Shared Report Inbox",
},
{
"folderId": 359057,
"folderName": "BE Shared Spec Inbox",
}]
How could i return this as exactly as my legacy code response. I know i can convert the List to Map and display. But, is there any equivalent
way.
Thanks.
You can put your result into a map.
#GetMapping("/folders")
public List<Folder> getAllFolders() {
return Service.findAllFolders(1,2);
}
Change to:
#GetMapping("/folders")
public Map<String,List<Folder>> getAllFolders() {
Map<String,List<Folder>> map = new HashMap<>();
map.put("folders",Service.findAllFolders(1,2));
return map;
}

mongo aggregation:- push all fields using Spring

if I have a collection of books :-
{author: "tolstoy", title:"war & peace", price:100, pages:800}
{author: "tolstoy", title:"Ivan Ilyich", price:50, pages:100}
and if i want a result like this after grouping them by author :-
{ author: "tolstoy",
books: [
{author: "tolstoy", title:"war & peace", price:100, pages:800}
{author: "tolstoy", title:"Ivan Ilyich", price:50, pages:100}
]
}
using raw mongo queries I can do something like this:-
{$group: {
_id: "$author",
books:{$push: {author:"$author", title:"$title", price:"$price", pages:"$pages"}},
}}
But how do I do this using spring , I tried something like this:-
private GroupOperation getGroupOperation() {
return group("author").push("title").as("title").push("price").as("price").push("pages").as("pages");
}
but this does not seem to work. Any help would be appreciated.
UPDATE:-
I used the solution as in the link suggested by #Veeram and it works great but I ran into another issue when I project it. I have my projection class which looks like:-
public class BookSummary{
private String author;
private List<Book> bookList;
//all getters and setters below
}
The group method looks like this:-
private GroupOperation getGroupOperation() {
return group("author").push(new BasicDBObject("id","$_id").append("title","$title").append("pages","$pages").append("price","$price")).as("bookList");
}
the projection method looks like this:-
private ProjectionOperation getProjectOperation() {
return project("author").and("bookList").as("bookList");
}
and the final aggregation operation:-
mongoTemplate.aggregate(Aggregation.newAggregation(groupOperation,projectionOperation), Book.class, BookSummary.class).getMappedResults();
However this gives the result:-
[
{
"author": null,
"bookList": [
{
"id": null,
"title": "title1",
"pages": "100",
"price":"some price"
},
{
"id": null,
"title": "title2",
"pages": "200",
"price":"some price"
}
]
}
]
Why is the author and id null here? Any help would be appreciated
You should be projecting using _id instead in the project phase.
private ProjectionOperation getProjectOperation() {
return project("_id").and("bookList").as("bookList");
}

Resources