I using Spring boot for REST application, I am getting null as DTO object even though the JSON request seems to be correct. Could anyone suggest something.
json from postman
{
"description": "Third questionnaire",
"createdDate": "2022-06-23",
"approvalStatus": "Approved",
"questionnaireVersion": "V1",
"isActive": false,
"questionSet": [
{
"text": "Question text",
"possibleAnswerSet": []
}
]
}
Controller post method is
#PostMapping(
produces = MediaType.APPLICATION_JSON_VALUE ,
consumes = MediaType.APPLICATION_JSON_VALUE
)
public String createQuestionnaire(#RequestBody QuestionnaireDTORequest questionnaireDTORequest){
Questionnaire questionnaire = mapToQuestionnaire(questionnaireDTORequest);
Questionnaire createdQuestionnaire = questionnaireService.createQuestionnaire(questionnaire);
if(createdQuestionnaire != null)
return "Questionnaire created successfully";
else
return "Questionnaire cannot be created";
}
when I run in debug mode i get questionnaireDTORequest as null.
QuestionnaireDTORequest
public class QuestionnaireDTORequest implements Serializable {
private static final long serialVersionUID = 5L;
private String description;
private LocalDate createdDate;
private String approvalStatus;
private String questionnaireVersion;
private boolean isActive = false;
private Set<Question> questionSet = new HashSet<>();
}
TL:DR Add getters and setters to QuestionnaireDTORequest.
Also, that will only make the values null, not the QuestionnaireDTORequest object. Is that the case?
I ran:
curl --location --request POST 'localhost:8081/' \
--header 'Content-Type: application/json' \
--data-raw ' {
"description": "Third questionnaire",
"createdDate": "2022-06-23",
"approvalStatus": "Approved",
"questionnaireVersion": "V1",
"isActive": false,
"questionSet": [
{
"text": "Question text" }
]
}'
My controller:
#RestController
public class MyController {
#PostMapping(
produces = MediaType.APPLICATION_JSON_VALUE ,
consumes = MediaType.APPLICATION_JSON_VALUE
)
public String createQuestionnaire(#RequestBody QuestionnaireDTORequest questionnaireDTORequest){
if(questionnaireDTORequest != null)
return "Questionnaire created successfully";
else
return "Questionnaire cannot be created";
}
}
The model was the same only added Getters and Setters.
Related
I'm in the process of developing a RESTful API. I want to get user details by calling API. But my expected response not included null values.
Expected Response
{
"id": 1,
"username": "admin",
"fullName": "Geeth Gamage",
"userRole": "ADMIN",
"empNo": null
}
Actual Response
{
"id": 1,
"username": "admin",
"fullName": "Geeth Gamage",
"userRole": "ADMIN"
}
My Spring boot rest API code is below. why not include null parameters for my response?
#GetMapping(value = "/{userCode}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> findUser(#PathVariable String userCode)
{
return ResponseEntity.status(HttpStatus.OK).body(userService.getUserByUserName(userCode));
}
#Override
#Transactional
public Object getUserByUserName(String username)
{
try {
User user = Optional.ofNullable(userRepository.findByUsername(username))
.orElseThrow(() -> new ObjectNotFoundException("User Not Found"));
return new ModelMapper().map(user, UserDTO.class);
} catch (ObjectNotFoundException ex) {
log.error("Exception : ", ex);
throw ex;
} catch (Exception ex) {
log.error("Exception : ", ex);
throw ex;
}
}
User entity class and UserDTO object class as below
User.class
#Data
#Entity
#Table(name = "user")
public class User{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
private Long id;
#Column(name = "USERNAME", nullable = false, length = 64)
private String username;
#Column(name = "USER_ROLE", nullable = false)
private String userRole;
#Column(name = "EMP_NO")
private String empNo;
}
UserDTO.class
#Data
public class UserDTO {
private Long id;
private String username;
private String fullName;
private String userRole;
private String empNo;
}
Assuming you are using Jackson you can configure its global behaviour using setSerializationInclusion(JsonInclude.Include) on the ObjectMapper.
For your use case you can configure it as NON_EMPTY or ALWAYS. For more details please check https://fasterxml.github.io/jackson-annotations/javadoc/2.6/com/fasterxml/jackson/annotation/JsonInclude.Include.html.
You can also do this at class or attribute level using the corresponding annotation #JsonInclude.
So, i have a List of my custom Object and I need a JSON like this:
{
"surveys": [{
"survey": {
"code": "05052017153632",
"date": "05/05/2017 15:36:32",
"device_id": 1,
"questions_attributes": [{
"kind": "string",
"label": "Você encontrou tudo o que procurava?",
"value": "Infelizmente, não"
}, {
"kind": "string",
"label": "Em qual departamento você não encontrou o produto?",
"value": "FERRAMENTAS, TAPETES"
}, {
"kind": "string",
"label": "Deseja que a Havan entre em contato com você?",
"value": "Não informado"
}, {
"kind": "string",
"label": "Nome",
"value": "Não informado"
}, {
"kind": "string",
"label": "E-mail",
"value": "Não informado"
}, {
"kind": "string",
"label": "Telefone",
"value": "Não informado"
}]
}
}]}
But I dont have any ideia how to do it using Gson.
I'm Using Retrofit 2 and need to pass this JSON into a body request.
Any ideias?
Yes you need to pass this JSON into body request.
Retrofit Interface:
public interface RetrofitInterface<R extends RetrofitClass> {
#Headers({"Content-Type: application/json", "Cache-Control: max-age=640000"})
#POST("v1/auth/")
public Call<ResponseBody> callLogin(#Query("key") String key, #Body LoginModel body);
#Headers({"Content-Type: application/json", "Cache-Control: max-age=640000"})
public static final Retrofit retrofit = new Retrofit.Builder()
.baseUrl(AppConstants.mBaseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
Api call Activity:
pass json object into body request use #Body param.
Here you can create gson model classes in http://www.jsonschema2pojo.org/
json to gson converter by using that json request format.
After that set values with that gson pojo classes and pass the json object to body request in retrofit.
For example:
LoginModel:
public class LoginModel {
#SerializedName("username")
private String username;
#SerializedName("password")
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
set Values with pojo class:
LoginModel model_obj = new LoginModel();
mModel_obj.setUsername(mUsername);
mModel_obj.setPassword(mPassword);
Api calling:
Call<ResponseBody> call = service.callLogin(AppConstants.mApiKey, model_obj);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
I saw this question that I made 1 year ago and I`m using another solution nowdays. If someone still needs a help and looking for this, here we go:
I have made a function to handle the JSON format that I want:
public String serialize(List<Object> objects, String arrKey, String
objKey) {
JsonArray ja = new JsonArray();
for (Object object: objects) {
Gson gson = new Gson();
JsonElement je = gson.toJsonTree(object);
JsonObject jo = new JsonObject();
jo.add(objKey, je);
ja.add(jo);
}
JsonObject objMain = new JsonObject();
objMain.add(arrKey,ja);
return objMain.toString();
}
And in my API Call I have this line:
String json = new CustomGsonAdapter().serialize(surveysList, "surveys","survey");
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
RequestBody is the trick. Now, just need to pass this RequestBody to retrofit call and thats it.
#POST("surveys")
Call<Void> setSurveys(#Body RequestBody json);
I dont know if this is the best way to archieve the problem, but for me it was.
Save time and avoid to create a class just to send to server.
#POST("users/new")
Call<User> createUser(#Body User user);
above code will be written in Api Service Interface.
and then you can call this from RestClient class(by Retrofit instance) by passing a JsonObject as Body.
I have Stock response class with only two fields as follows
class StockResponse {
private String orderId;
private String status;
//constructor
//getters and setters
}
and the also the controller as follows
#RestController
#RequestMapping("/stocks")
public class StockController {
private static List<StockResponse> stocktList = new ArrayList <StockResponse > ();
static {
stocktList.add(new StockResponse("order1", "AVAILABLE"));
stocktList.add(new StockResponse("order2", "AVAILABLE"));
stocktList.add(new StockResponse("order3", "NOT AVAILABLE"));
stocktList.add(new StockResponse("order4", "AVAILABLE"));
}
#GetMapping("/")
public ResponseEntity < ? > getProsucts() {
return ResponseEntity.ok(stocktList);
}
#GetMapping(path="/{id}", produces = "application/json;charset=UTF-8")
public StockResponse getProsucts(#PathVariable String id) {
StockResponse product = findOrder(id);
if (product == null) {
// return ResponseEntity.badRequest(product)
// .body("Invalid product Id");
}
System.out.println(product.getOrderId());
System.out.println(product.getStatus());
return new StockResponse(product.getOrderId(), product.getStatus());
}
private StockResponse findOrder(String id) {
return stocktList.stream()
.filter(user -> user.getOrderId()
.equals(id))
.findFirst()
.orElse(null);
}
}
when I make a call to the localhost:8082/stocks/order1 I get a response with only one field showing up as follows
what could I be missing out?
I am unable to reproduce this, meaning it works for me.
Listing all stocks
$ curl -sS 'http://localhost:8080/stocks/' | jq "."
[
{
"orderId": "order1",
"status": "AVAILABLE"
},
{
"orderId": "order2",
"status": "AVAILABLE"
},
{
"orderId": "order3",
"status": "NOT AVAILABLE"
},
{
"orderId": "order4",
"status": "AVAILABLE"
}
]
Getting specific stock
$ curl -sS 'http://localhost:8080/stocks/order1' | jq "."
{
"orderId": "order1",
"status": "AVAILABLE"
}
My StockController is identical to yours. I also copy and pasted your StockResponse to get the field names identical to yours, but since you didn't include the constructor/getter and setter I'll show the one which works for me.
The way you're instantiating the StockResponse objects in the stocktList list is using the constructor and that might point to that you're not actually setting the this.status on the objects. If that doesn't work, double check that the getter for the status field is actually called getStatus().
public class StockResponse {
private String orderId;
private String status;
public StockResponse(String orderId, String status) {
this.orderId = orderId;
this.status = status;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
The fact that your response contains a field that uses "non-standard" upper case for the first letter tells me that maybe you're doing something else that's not standard the might influence your result.
I ensured that all my getters and setters are public,
I looked closely and I had missed public on
public String getStatus()
This is actually a two part question, one is, how to pass two JSON objects, anther one is how to create object from two different classand pass that object to a POST method.
I am trying to test a POST method of RESTful API by passing two different objects in postman. I could successfully test only a POST method with one object, but when I am trying to send two different object to POST method where two different objects , postman say's bad request.
Here is my two entity class: First one is Cleint class:
import lombok.Data;
import org.zevgma.api.entities.user.User;
import javax.persistence.*;
import java.util.Date;
#Data
#Entity
#Table(name = "client")
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_pk_generator")
#SequenceGenerator(name = "user_pk_generator", sequenceName = "users_user_id_seq", allocationSize = 1)
#Column(name = "id", unique = true, nullable = false, columnDefinition = "serial")
private int id;
#Column(name = "name", nullable = false)
private String name;
#Column(nullable = false)
private String email;
#Column(nullable = false)
private Date userDateCreate;
}
Second one is about ClientAddress:
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Data
#Entity
public class ClientAddress {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private int streetID;
private String streetName;
private int streetNumber;
}
I have tried different POST request with JSON objets or JSON nested threads in Stackoverflow and youtube, but could not find my solution.
Spceially I have tried instructions from these threads:
https://it.mathworks.com/help/thingspeak/bulkwritejsondata.html
Post Multiple JSON Objects Simultaneously with Express and Postman
Here is the POST method of RESTful API which one I want to test:
#PostMapping(value = "/api/employees", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> createClient(#RequestBody Client client,
#RequestBody ClientAddress clientAddress){
clientService.save(client);
clientAddressService.save(clientAddress);
return new ResponseEntity<>(client.getName() + " and "+ clientAddress.getStreetName() + " successfully added",
HttpStatus.OK);
}
So far I have tried with this JSON object:
[
{
"name": "Bilbo",
"email": "bilbo#gmail.com"
},
{
"streetName": "Central Street",
"streetNumber": "31"
}
]
[
{
"client":{
"name": "Bilbo",
"email": "bilbo#gmail.com"
},
"clientAddress":{
"streetName": "Central Street",
"streetNumber": "31"
}
}
]
By Pre-Request Script:
//Pre-request Script
var client = {
"name": "Bilbo",
"email": "bilbo#mail.com"
}
var address = {
"streetName": "New Street",
"streetNumber": "3"
}
pm.environment.set("client", JSON.stringify(client));
pm.environment.set("address", JSON.stringify(address));
//Body
[
{{client}},{{address}}
]
Now the second part of questions: What if I want to create a new object, ClientData from the above two mentioned class, Client and ClientAddress and pass this ClientData object to a POST method, how to do it.
Here is the ClientData class:
import lombok.Data;
#Data
public class ClientData {
private Client client;
private ClientAddress address;
}
And here is the POST method where I want to pass a ClientData object:
#PostMapping(value = "/api/employees", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> createClient(#RequestBody ClientData clientData){
Client client = clientData.getClient();
clientService.save(client);
ClientAddress clientAddress = clientData.getAddress();
clientAddressService.save(clientAddress);
return new ResponseEntity<>(client.getName() + " and "+ clientAddress.getStreetName() + " successfully added",
HttpStatus.OK);
}
To test method in postman I should create these complex ClientData JSON object and how to pass the it?
Your ClientData class is
#Data
public class ClientData {
private Client client;
private ClientAddress address;
}
So you request payload should look like
{
"client":{
"name": "Bilbo",
"email": "bilbo#gmail.com"
},
"address":{
"streetName": "Central Street",
"streetNumber": "31"
}
}
Notice address instead of clientAddress.
Also you don't need to pass it as array it will be just a json object (not [])
Instead of doing this in the body:
[{{client}}, {{address}}]
Replace it with this:
{{requestBody}}
Now, store the complete body in an environment variable.
Remove the separate variables for client and address and just use a single variable.
So, update your pre-request script to something like this:
pm.environment.set("requestBody", JSON.stringify([client, address]));
I have a project (homework) about JAX-RS. I'm working with NetBeans, Jersey and Tomcat. In the post method for example:
'{"user":{"username":"accavdar", "gender":"M", "birthDate":"06.11.1982"}}'
when such a request comes I have to parse this input and add new user to my system. The sample response must be:
{
"meta": {
"code": 200
},
"data": {
"message": "You successfully created a user."
}
}
Expectable error can be like that:
{
"meta": {
"code": 101,
"type": "FieldError",
"errors": [
{
"fieldName": "fullname",
"rejectedValue": null
}
]
}
}
Another problem is that: With using Get method the develepor can want to list all user in the system. The response must be like that :
{
"meta": {
"code": 200
},
"data": [
{
"id": 1,
"username": "sample",
"fullname": "sample",
"gender": "M",
"birthDate": "12.02.1990"
},
{
"id": 2,
"username": "sample",
"fullname": "sample",
"gender": "M",
"birthDate": "21.09.1940"
}
]
}
I want to keep users in a text file, there is no limitation about the way of keeping users.(You can keep in database or in memory) But I have no idea about how to handle request input and generate a response like that. I don't want you to do my homework but could anyone give tips about my problem, please?
NOTE: We will work only with JSON "Content-Type: application/json" "Accept: application/json"
EDİT: #Bogdan , Thank you very much for your answer. I searched the web sites you gave. I want to learn that how is that output produced?:
{
"meta": {
"code": 200
},
"data": {
"message": "You successfully created a user."
}
}
or
{
"meta": {
"code": 200
},
"data": {
"id": 1,
"username": "jack",
"fullname": "jack",
"gender": "M",
"birthDate": "12.12.2012"
}
}
I have "meta" and "user" classes.
#XmlRootElement(name="data")
public class User {
#XmlElement
public int id ;
#XmlElement
public String username;
#XmlElement
public String fullname;
#XmlElement
public String gender;
#XmlElement
public String birthDate;
public User(){
}
#XmlRootElement(name="meta")
public class Meta {
#XmlElement
int code=200;
public Meta(){
}
Also I have this jaxbcontextresolver class
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext>{
private JAXBContext context;
private Class[] types = {User.class, Meta.class};
public JAXBContextResolver() throws Exception {
this.context =
new JSONJAXBContext( JSONConfiguration.mapped().nonStrings("id").nonStrings("code").build(), types);
}
#Override
public JAXBContext getContext(Class<?> objectType) {
for (Class type : types) {
if (type == objectType) {
return context;
}
}
return null;
}
}
but how to create this response constructs, could any help me?
Your application works with users. This is the resource that you application deals with and your client interacts with for creating, updating, deleting and getting (basically CRUD).
But a user is an abstract resource so your server and client interact with each other by using representations of this resource. The representations can be in JSON format (as in your example), XML etc. Your client specifies what type of representation it wants and the server specifies the representation type it returns by means of the Content-Type. The body of the request/response matches the content type.
This is a Java application so in your application code the users are represented as Java objects. You need to transform the request/response body into objects with getters and setters. You can use JAXB to do the XML transformation, and with a library like Jackson you can transform JSON. You can off course do it manually and parse strings into objects and output strings from objects but that would be more work.
A client submits JSON and after transformation you will have your Java objects that you can handle. You can keep them in memory inside an application scope map or write them inside a file or database and change their representation once again.
Your application will bind URLs to specific actions that transform the request representation into objects, perform operations on the objects then return them to be transformed again into the representation the client expects.
The above are just basic explanations. All your questions can be answered if you follow some JAX-RS tutorials (e.g. a quick search returns REST with Java (JAX-RS) using Jersey - Tutorial or REST: CRUD with JAX-RS (Jersey). I'm sure there are lots other). Your question is too open ended so just dig in and return with specific questions on stackoverflow when you hit road blocks.
EDIT : seems you are struggling with this a little so I'll add a basic service to get you started, let's say for the list of users.
You didn't mention so far nothing about your service class. That's the important one, something like:
package com.test;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/api")
public class Test {
#GET
#Path("/users")
#Produces({ MediaType.APPLICATION_JSON })
public UsersResponseWrapper getUsers() {
List<User> users = new ArrayList<User>();
User u1 = new User();
u1.setId(1);
u1.setFullname("Joe Doe");
u1.setGender("M");
u1.setUsername("joe.doe");
u1.setBirthDate("1919-12-12");
User u2 = new User();
u2.setId(1);
u2.setFullname("John Smith");
u2.setGender("M");
u2.setUsername("john.smith");
u2.setBirthDate("1990-01-01");
users.add(u1);
users.add(u2);
UsersResponseWrapper resp = new UsersResponseWrapper();
resp.setMeta(new Meta(200));
resp.setData(users);
return resp;
}
}
Then your user and meta classes:
package com.test;
public class Meta {
private int code;
public Meta(int code) {
this.code = code;
}
public Meta() {
this.code = 200;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
package com.test;
public class User {
private int id;
private String username;
private String fullname;
private String gender;
private String birthDate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFullname() {
return fullname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getBirthDate() {
return birthDate;
}
public void setBirthDate(String birthDate) {
this.birthDate = birthDate;
}
}
A JAXB provider:
package com.test;
import java.util.ArrayList;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
private static Class<?>[] types = {UsersResponseWrapper.class, User.class, Meta.class, ArrayList.class};
public JAXBContextResolver() throws Exception {
this.context = JAXBContext.newInstance(types);
}
#Override
public JAXBContext getContext(Class<?> objectType) {
for (Class<?> type : types) {
if (type == objectType) {
return context;
}
}
return null;
}
}
Something from web.xml:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>RestService</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.test</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>RestService</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
And a wrapper for your result:
package com.test;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class UsersResponseWrapper {
private Meta meta;
private List<User> data;
public Meta getMeta() {
return meta;
}
public void setMeta(Meta meta) {
this.meta = meta;
}
public List<User> getData() {
return data;
}
public void setData(List<User> data) {
this.data = data;
}
}
I think this last class is what put you in difficulty since your result is formed of both meta content and data content. Remember you need to return objects (the default Jackson mapper from Jersey distribution will then take care of it) and it happens you have a complex one. The above should return this (formatting not included):
{
"data": [
{
"birthDate": "1919-12-12",
"fullname": "Joe Doe",
"gender": "M",
"id": "1",
"username": "joe.doe"
},
{
"birthDate": "1990-01-01",
"fullname": "John Smith",
"gender": "M",
"id": "1",
"username": "john.smith"
}
],
"meta": {
"code": "200"
}
}
That's about as much I can add to this as details. It's your homework after all :). You are doing fine, just keep going.