java jpa json standard - java

First, thank you very much for reading this question.
I have a JPA project and everything works fine, the json that i get with the controller is of this form:
{"id": 1, "name": "Canada"},{"id": 2, "name": "USA"}
All its fine but i would like to get a json with the Jsend standard, it something like this:
{
status : "success",
data : {
"country" : [
{"id": 1, "name": "Canada"},
{"id": 2, "name": "USA"}
]
}
}
{
"status" : "fail",
"data" : { "title" : "A title is required" }
}
{
"status" : "error",
"message" : "Unable to communicate with database"
}
As you can see i want to have a status that says success, fail or error:
But i dont know how to do it. This is my DTO, DAO and Controller
#Entity
public class Country implements Serializable {
private static final long serialVersionUID = -7256468460105939L;
#Id
#Column(name="id")
private int id;
#Column(name="name")
private String name;
//Constructor, get and set
DAO
#Repository
#Transactional
public class CountryRepository {
#PersistenceContext
EntityManager entityManager;
public CountryDTO findById(int id) {
return entityManager.find(CountryDTO.class, id);
}
}
Controller
#RestController
public class CountryController {
#Autowired
CountryDTO repository;
#RequestMapping(value="api/country/{id}", method=RequestMethod.GET)
public #ResponseBody CountryDTO getByID(#PathVariable("id") int id){
return repository.findById(id);
}
}
Again thank you for your time.

Its very good question from my point of view. So I can give list of action items to achieve this.
You should aware of #ControllerAdvice annotation which is available in Spring.
By utilizing that you can play with your response object.
Then You should create your own Object which is similar to JSend. In my case, I have created JSendMessage class
public class JSendMessage {
String status;
String message;
Object data;
// Add getter and setter
}
Now you should map above class with your #ControllerAdvice return your required object.
So whenever there is a exception you can create and send your own custom exception message.
There will be lot of reference for this. Just look for #ControllerAdvice

Related

Options to return a custom complex object from a spring data jpa

I'm trying to get the data from MSSQL database using below logic. Couldnt identify the option to prepare a single query (SQL or JPQL) to get the repository reponse as MyTestSTO.Can someone suggest me the options available to acheive this.
Expected object Format :
{
"id": "1",
"name": "Test",
"CabInfo": {
"CabStatus": 0,
"CabStatusStr": "Active",
"CabFunctionalArea": {
"FunctionalAreaId": 3
},
"CabApplication": {
"ApplicationId": 333,
"ApplicationAcronym": "OBM Router"
}
}
}
And JPARepository Class is :
public interface FloorPlanRepositoryExt extends JpaRepository<Sites, Long> {
//Other APIs on Sites Table.
Can we use multiple news in below query ?
#Query(value = "select new TestDTo(sp.id,sp.name,sp.cabStatus,sp.cabStatusStr,
sp.functionalArea,sp.applicationId,sp.ApplicationAcronym) from SpacePower sp where sp.roomId=:roomId)
public List<TestDTo> getRoomSpacesCabByRoomId(#Param("roomId")Integer roomId);
}
My custom DTO is below:
class TestDTo implements serializable{
private String id;
privateString name;
private CabInfo cabInfo
//Setters and getters and Constructor
}
class CabInfo{
private String cabStatus;
private String CabStatusStr;
private CabFunctionalArea cabFunctionalArea;
private CabApplication CabApplication;
//Setters and getters and Constructor
}
In select query you expect TestDTo but in return method you define RoomSpacesCabDTO

Map Complex Json to Pojo Class

I am sending the following request (using Spring Boot)
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, request, String.class);
response is the json object(i have ommitted lot of fields in json object)
{
"customer": {
"id": 100,
"ci": {
"lDb": "11",
"localId": "1"
},
"cusdata": {},
"rating": {
"id": 3140,
"dateTime": "2019-09-21 06:45:41.10",
"rawData": {
"seg": "XYZ",
"seg2": "XYZ",
"et": "XYZ_CORP",
"CountryCodes": [
"IN"
],
"doBusiness": "2017-09-20"
],
...
....
...
...
"status": "SUCCESS"
}
I need to map the below fields to a Pojo Class
1.localId
2.seg
3.seg2
4.status
How can i create the PojoClass such that those fields are mapped automatically
So basically how will my PojoClass should look like?
ResponseEntity<PojoClass> response = restTemplate.exchange(url, HttpMethod.GET, request, PojoClass.class);
I suggest that you use sites like http://www.jsonschema2pojo.org/. There, you can select many options on the right panel and adjust POJO you want to get from JSON schema.
Your PojoClass has to follow the structure of the JSON that your are receiving and have the fields that your are interested (or all of them).
For the first level class:
public class PojoClass {
private Customer customer;
private String status;
...
}
Then, create a Customer class for the customer fields and create more classes for the rest of the fields:
public class Customer {
public String id;
public CI ci;
public CustData custData;
...
}
Create a custom class PojoClass
public class PojoClass {
private Integer id;
private Object ci;
private Object cusdata;
private Object rating;
private Object status;
}
ResponseEntity<PojoClass> responseEntity = restTemplate.exchange(url,HttpMethod.GET,request,new ParameterizedTypeReference<PojoClass>(){
});

How to validate a collection in spring-mvc POST webservice?

I have a simple spring webservice that offers a #PostMapping and takes a json array of elements.
I want spring to automatically validate each element in the list.
#RestController
public class PersonServlet {
#PostMapping
public void insertPersons(#RequestBody #Valid List<PersonDto> array) {
}
}
public class PersonDto {
#NotBlank
private String firstname;
#NotBlank
private String lastname;
}
The following POST request should fail with a validation error that firstname is missing:
[
{
"lastname": "john"
},
{
"firstname": "jane",
"lastname": "doe"
}
]
Result: the request is NOT rejected. Why?
Sidenote: if I just use PersonDto as parameter (not a list), and send a json post request with only one persons, the validation works and correctly rejects the request.
So in general the validation annotations seem to work, but just not when inside the collection!
Workaround: the following triggers the list validation:
public class PersonDtoList extends ArrayList<PersonDto> {
#Valid
public List<PersonDto> getList() {
return this;
}
}
public void insertPersons(#RequestBody #Valid PersonDtoList array) {
}
You should add another class outside the list, for example PostCommand:
public class PostCommand() {
#Valid
private List<PersonDTO> list;
}
and send it on the request:
#RestController
public class PersonServlet {
#PostMapping
public void insertPersons(#RequestBody #Valid PostCommand postCommand) {
}
}
and JSON will be:
{
"list": [
{
"lastname": "john"
},
{
"firstname": "jane",
"lastname": "doe"
}
]
}
And you will have an exception.

How to create xml webservice DTOs depending on conditions?

Is it possible to create a global DTO for an xml webservice, but having conditional fields inside?
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class MyDTO {
...
#XmlElementWrapper
#XmlElement(name = "somename")
private List<String> list;
}
Now, what if I want to release another version of the webservice, and rename the #XmlElement field thereby (or introduce additional fields, remove some, etc).
So that backwards compatibility is retained, but the same objects are used for the "new" version.
I could maybe do this my adding request path methods with /v1, /v2 etc.
But how could I then maintain a single DTO class, but with fields conditional on the version path?
Or would I always have to duplicate those DTO classes and modify exactly to me needs of the version?
#RestController
public void MyServlet {
#RequestMapping("/v1")
public MyDTO1 request1() {
}
#RequestMapping("/v2")
public MyDTO2 request2() {
}
}
I would prefer using tailored DTOs for each version of the API. To avoid boilerplate code when mapping your entities to DTOs, you could consider using mapping frameworks such as MapStruct.
If you are using Jackson, you could consider using JSON Views (they will work with XML too). Quoting from the Latest Jackson integration improvements in Spring article:
JSON Views
It can sometimes be useful to filter contextually objects serialized to the HTTP response body. In order to provide such capabilities, Spring MVC now has builtin support for Jackson’s Serialization Views (as of Spring Framework 4.2, JSON Views are supported on #MessageMapping handler methods as well).
The following example illustrates how to use #JsonView to filter fields depending on the context of serialization - e.g. getting a “summary” view when dealing with collections, and getting a full representation when dealing with a single resource:
public class View {
interface Summary {}
}
public class User {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private String firstname;
#JsonView(View.Summary.class)
private String lastname;
private String email;
private String address;
private String postalCode;
private String city;
private String country;
}
public class Message {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private LocalDate created;
#JsonView(View.Summary.class)
private String title;
#JsonView(View.Summary.class)
private User author;
private List<User> recipients;
private String body;
}
Thanks to Spring MVC #JsonView support, it is possible to choose, on a per handler method basis, which field should be serialized:
#RestController
public class MessageController {
#Autowired
private MessageService messageService;
#JsonView(View.Summary.class)
#RequestMapping("/")
public List<Message> getAllMessages() {
return messageService.getAll();
}
#RequestMapping("/{id}")
public Message getMessage(#PathVariable Long id) {
return messageService.get(id);
}
}
In this example, if all messages are retrieved, only the most important fields are serialized thanks to the getAllMessages() method annotated with #JsonView(View.Summary.class):
[ {
"id" : 1,
"created" : "2014-11-14",
"title" : "Info",
"author" : {
"id" : 1,
"firstname" : "Brian",
"lastname" : "Clozel"
}
}, {
"id" : 2,
"created" : "2014-11-14",
"title" : "Warning",
"author" : {
"id" : 2,
"firstname" : "Stéphane",
"lastname" : "Nicoll"
}
}, {
"id" : 3,
"created" : "2014-11-14",
"title" : "Alert",
"author" : {
"id" : 3,
"firstname" : "Rossen",
"lastname" : "Stoyanchev"
}
} ]
In Spring MVC default configuration, MapperFeature.DEFAULT_VIEW_INCLUSION is set to false. That means that when enabling a JSON View, non annotated fields or properties like body or recipients are not serialized.
When a specific Message is retrieved using the getMessage() handler method (no JSON View specified), all fields are serialized as expected:
{
"id" : 1,
"created" : "2014-11-14",
"title" : "Info",
"body" : "This is an information message",
"author" : {
"id" : 1,
"firstname" : "Brian",
"lastname" : "Clozel",
"email" : "bclozel#pivotal.io",
"address" : "1 Jaures street",
"postalCode" : "69003",
"city" : "Lyon",
"country" : "France"
},
"recipients" : [ {
"id" : 2,
"firstname" : "Stéphane",
"lastname" : "Nicoll",
"email" : "snicoll#pivotal.io",
"address" : "42 Obama street",
"postalCode" : "1000",
"city" : "Brussel",
"country" : "Belgium"
}, {
"id" : 3,
"firstname" : "Rossen",
"lastname" : "Stoyanchev",
"email" : "rstoyanchev#pivotal.io",
"address" : "3 Warren street",
"postalCode" : "10011",
"city" : "New York",
"country" : "USA"
} ]
}
Only one class or interface can be specified with the #JsonView annotation, but you can use inheritance to represent JSON View hierarchies (if a field is part of a JSON View, it will be also part of parent view). For example, this handler method will serialize fields annotated with #JsonView(View.Summary.class) and #JsonView(View.SummaryWithRecipients.class):
public class View {
interface Summary {}
interface SummaryWithRecipients extends Summary {}
}
public class Message {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private LocalDate created;
#JsonView(View.Summary.class)
private String title;
#JsonView(View.Summary.class)
private User author;
#JsonView(View.SummaryWithRecipients.class)
private List<User> recipients;
private String body;
}
#RestController
public class MessageController {
#Autowired
private MessageService messageService;
#JsonView(View.SummaryWithRecipients.class)
#RequestMapping("/with-recipients")
public List<Message> getAllMessagesWithRecipients() {
return messageService.getAll();
}
}

How to bind map of custom objects using #RequestBody from JSON

I need to send map of custom objects Map<String, Set<Result>> from frontend to backend.
So I think it should be possible to build JSON, send it to Controller via Ajax and receive it in Controller via #RequestBody annotation which should bind json to object. right?
Controller:
#RequestMapping(value = "/downloadReport", method = RequestMethod.POST)
public ResponseEntity<byte[]> getReport(#RequestBody Map<String, Set<Result>> resultMap)
{
Context context = new Context();
context.setVariable("resultMap", resultMap);
return createPDF("pdf-report", context);
}
JSON:
{
"result": [
{
"id": 1,
"item": {
"id": 3850,
"name": "iti"
},
"severity": "low",
"code": "A-M-01",
"row": 1,
"column": 1,
"description": "Miscellaneous warning"
}
]
}
Model:
public class Result {
private Integer id;
private Item item;
private String severity;
private String code;
private Integer row;
private Integer column;
private String description;
//getter & setters
//hashCode & equals
}
public class Item {
private Integer id;
private String name;
//getter & setters
}
After send such a JSON like above by ajax I am getting error message from browser:
The request sent by the client was syntactically incorrect
If I change JSON to send empty set like below then it works but of course my map has empty set:
{"result": []}
So, Why I am not able to receive filled map with set of objects? Why binding/unmarshalling do not work as expected and what I should do to make it works?
Note:
I am using Jackson library and marshalling for other case for #ResponseBody works fine. Problem is with unmarshalling and binding object via #RequestBody.
In order for jackson to properly deserialize your custom classes you need to provide #JsonCreator annotated constructor that follows one of the rules defined in the java doc. So for your Item class it could look like this:
#JsonCreator
public Item(#JsonProperty("id") Integer id,
#JsonProperty("name") String name) {
this.id = id;
this.name = name;
}
you have to deal with map differently,
first create wrapper class
public MyWrapperClass implements Serializable {
private static final long serialVersionUID = 1L;
Map<String, List<String>> fil = new HashMap<String, List<String>>();
// getters and setters
}
then you should take request in controller,
#PostMapping
public Map<String,List<String>> get(#RequestBody Filter filter){
System.out.println(filter);
}
Json Request should be like
{
"fil":{
"key":[
"value1",
"value2"
],
"key":[
"vakue1"
]
}
}

Categories