I have an object like this.
public class Profile {
private String email;
private String phone;
#SerializedName("userCode")
private String user_code;
public String getEmail() {
return email;
}
public String getPhone() {
return phone;
}
public String getUser_code() {
return user_code;
}
}
This is what I got when I return that object in Rest API
{
"email": "abc#gmail.com",
"phone": 12345678,
"user_code": "742aeaefac"
}
Apparently annotation #SerializedName here did not work, I can understand that it get the object properties name base on its getter name, not in the annotation. If I change the getter name into getUserCode(), it will work as I expected.
I also try to use #JsonProperty but didn't help too.
Can someone explain what is the work here to solve this?
Update the code for serialization process in the controller.
#PostMapping(path = "/login", produces = "application/json")
#ResponseBody
public ClientRepresentation login(#RequestBody LoginRequest login) {
Map<String, Object> resObj = new HashMap<String, Object>();
ProfileResponse profileResponse = userService.findUserProfileByUserCode(login.getUserCode());
//Code logic to process object data...
resObj.put("profile", profileResponse);
return ClientRepresentationBuilder.buildClientRep(HttpStatus.OK.value(), "Success", resObj);
}
ClientRepresentation class
public class ClientRepresentation implements Serializable {
private Integer code;
private String message;
private Object data;
}
Related
The browser sends the following object to the backend:
Now I would like to store the data in my database. So I have an entity that looks like this:
#Entity
public class NewQuote {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String description;
#ElementCollection(targetClass = Details.class)
private List<Details> details = new ArrayList<>();
public NewQuote(String description, ArrayList<Details> details) {
this.description = description;
this.details = details;
}
#Embeddable
public class Details {
private String description;
private String label;
public Details(String description, String label) {
this.description = description;
this.label = label;
}
public Details() {}
}
Controller
#PostMapping(value = "/save-quote", produces = MediaType.APPLICATION_JSON_VALUE)
public void saveQuote(#RequestBody Map<String, String> data) {
newQuoteService.saveQuote(data);
}
Service
public void saveQuote(Map<String, String> data) {
JSONObject json = new JSONObject(data);
NewQuote newQuote = new NewQuote(
json.getAsString("description"),
json.getAsString("details")
);
newQuoteRepository.save(newQuote);
}
I am getting an error because json.getAsString("details") should not be a string of course. So how can I turn it to ArrayList<Details>?
Add a DTO to manage your json response. You don't need to explicitly use JSONObject because spring already manage the process of mapping under the wood with Jackson.
Also, it is not a good practice to pass your Entities directly into the controller methods.
NewQuoteDto.java
public class NewQuoteDto {
private Long id;
private String description;
private List<Details> details = new ArrayList<>();
public NewQuoteDto() {
}
// getter and setter or you can use lombok
}
DetailDto.java
public class DetailDto {
private String description;
private String label;
public DetailDto() {}
// getter and setter or you can use lombok
}
Controller
#PostMapping(value = "/save-quote", produces = MediaType.APPLICATION_JSON_VALUE)
public void saveQuote(#RequestBody NewQuoteDto dataDto) {
// here you map dataDto to your model before you call saveQuote
NewQuote data = mapper.map(dataDto, NewQuote.class); // if you use ModelMapper library.
newQuoteService.saveQuote(data);
}
For custom mapping take look here.
I'm doing an API that consumes an external API (https://swapi.dev/), but I'm receving this error when I try to return the JSON converted to Object.
I'm trying to solve this for almost 12 hours without any success x_x
Error while extracting response for type [class [Lcom.starwarsapi.filmsapi.model.FilmModel;] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `[Lcom.starwarsapi.filmsapi.model.FilmModel;` from Object value (token `JsonToken.START_OBJECT`);
FilmController:
#RestController
#RequestMapping("/films")
public class FilmController {
#Autowired
private FilmService filmService;
#GetMapping
public List<FilmModel> getAllFilms() {
List<FilmModel> response = filmService.getAllFilms();
return response;
}
FilmModel:
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class FilmModel {
private FilmResultModel[] results;
}
FilmResultModel
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class FilmResultModel {
private String title;
#JsonProperty("episode_id")
private Integer episodeId;
#JsonProperty("opening_crawl")
private String description;
private String director;
private String producer;
#JsonProperty("release_date")
private String releaseData;
private String[] characters;
private String[] planets;
private String[] starships;
private String[] vehicles;
private String[] species;
private String created;
private String edited;
private String url;
FilmService:
public interface FilmService {
public List<FilmModel> getAllFilms();
}
FilmServiceImpl:
#Service
public class FilmServiceImpl implements FilmService {
#Value("${external.api.url}")
private String filmBaseUrl;
#Autowired
private RestTemplate restTemplate;
#Override
public List<FilmModel> getAllFilms() {
FilmModel[] result = restTemplate.getForObject(filmBaseUrl, FilmModel[].class);
List<FilmModel> films = Arrays.asList(result);
System.out.println(films);
return films;
}
PS¹: external.api.url = https://swapi.dev/api/films/?format=json
PS²: When I return getAllFilms as String, the program works:
#Override
public String getAllFilms() {
String result = restTemplate.getForObject(filmBaseUrl, String.class);
System.out.println(result);
return result;
}
But I need it to return as an object because later I'll try to create a PUT method to change the description of the movie.
try using FilmModel result = restTemplate.getForObject(filmBaseUrl, FilmModel.class);
FilmModel is just the outer wrapper object and contains the list of films.
Below is the object that I want to convert to JSON;
public class TestDto{
private ResponseType responseType;
private Long id;
private String name;
}
The ResponseType below is an enum;
public enum ResponseType{
TEST1("test message 1"), TEST2("test message 2"), TEST3("test message 3");
private String message;
}
Below is the JSON which I want to create:
{"code":"TEST1", "message":"test message 1", "id":1, "name":"name"}
and code in the JSON response points the name of the enum and the message in the JSON response points the message field of the enum.
Is there any way to do it?
Easiest way to do this is to add derived getters/setters to TestDto, and suppress JSON serialization of the responseType field.
class TestDto {
private ResponseType responseType;
private Long id;
private String name;
#JsonIgnore // Suppress JSON serialization
public ResponseType getResponseType() {
return this.responseType;
}
public void setResponseType(ResponseType responseType) {
this.responseType = responseType;
}
public String getCode() { // Derived getter for "code" property
return this.responseType.name();
}
public void setCode(String code) { // Derived setter for "code" property
this.responseType = (code == null ? null : ResponseType.valueOf(code));
}
public String getMessage() { // Derived getter for "message" property
return this.responseType.getMessage();
}
#Deprecated // Shouldn't be called by Java code, since it's a dummy stub method
#SuppressWarnings("unused")
public void setMessage(String message) { // Derived setter for "message" property
// Ignore value
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
Two-way test
TestDto testDto = new TestDto();
testDto.setResponseType(ResponseType.TEST1);
testDto.setId(1L);
testDto.setName("name");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(testDto);
System.out.println(json);
TestDto testDto2 = mapper.readValue(json, TestDto.class);
System.out.println(testDto2.getResponseType());
System.out.println(testDto2.getId());
System.out.println(testDto2.getName());
Output
{"id":1,"name":"name","message":"test message 1","code":"TEST1"}
TEST1
1
name
I have updated my previous answer as it wasn't correct. You can use #JsonFormat(shape = JsonFormat.Shape.OBJECT) to indicate that the enum should be serialized like an object (based on the getters).
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum ResponseType{
TEST1("test message 1"), TEST2("test message 2"), TEST3("test message 3");
private String message;
ResponseType(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public String getCode() {
return this.toString();
}
}
After that, you must also use #JsonUnwrapped on the Enum field to avoid having it's fields serialized as an object.
public static class TestDto {
#JsonUnwrapped private ResponseType responseType;
private Long id;
private String name;
}
Running the following code
TestDto testDto = new TestDto(ResponseType.TEST1, 1234356L, "First Response");
result = mapper.writeValueAsString(testDto);
System.out.println(result);
I get the result {"message":"test message 1","code":"TEST1","id":1234356,"name":"First Response"}
I have an endpoint in spring boot that consumes this JSON as an example:
{
"userId": 3,
"postBody": "This is the body of a post",
"postTitle": "This is the title of a post",
"created": null,
"tagList": ["tag1", "tag2", "tag3"]
}
The endpoint:
#RequestMapping(value="/newPost", method = RequestMethod.POST, produces="application/json", consumes = "application/json")
#ResponseBody
public ResponseEntity newPost(#RequestBody Map<String, Object> body) throws Exception {
I know the issue here is the Request body is being saved as a Map of objects which is fine for all the other attributes except the tagList. How can I get tagList to be an array of Strings in Java?
Thanks.
A mixutre of Ankur and Jose's answers solved this, thanks for the fast responses guys!
You should probably create a Java class which represents the input JSON and use it in the method newPost(.....). For example:-
public class UserPostInfo {
private int userId;
private String postBody;
private String postTitle;
private Date created;
private List<String> tagList;
}
Also, include the getter/setter methods in this class.
If you want to modify the behavior of JSON parsing, you can use Annotations to change field names, include only non-null values, and stuff like this.
If you don't want to use a custom POJO you could also just handle the deserialization into a Map yourself. Just have your controller accept a String and then use Jackson's ObjectMapper along with TypeReference to get a map.
#RequestMapping(value="/newPost", method = RequestMethod.POST, produces="application/json", consumes = "application/json")
#ResponseBody
public ResponseEntity newPost(#RequestBody String body) throws Exception {
ObjectMapper mapper = new ObjectMapper();
TypeReference<HashMap<String,Object>> typeRef = new TypeReference<HashMap<String,Object>>() {};
HashMap<String,Object> map = mapper.readValue(body, typeRef);
}
The resulting HashMap will use an ArrayList for the tag list:
You can create a custom Java POJO for the request that uses String[] versus List<String>. Here I did it for you using the site jsonschema2pojo.
package com.stackoverflow.question;
import com.fasterxml.jackson.annotation.*;
import java.util.HashMap;
import java.util.Map;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"userId",
"postBody",
"postTitle",
"created",
"tagList"
})
public class MyRequest {
#JsonProperty("userId")
private int userId;
#JsonProperty("postBody")
private String postBody;
#JsonProperty("postTitle")
private String postTitle;
#JsonProperty("created")
private Object created;
#JsonProperty("tagList")
private String[] tagList = null;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("userId")
public int getUserId() {
return userId;
}
#JsonProperty("userId")
public void setUserId(int userId) {
this.userId = userId;
}
#JsonProperty("postBody")
public String getPostBody() {
return postBody;
}
#JsonProperty("postBody")
public void setPostBody(String postBody) {
this.postBody = postBody;
}
#JsonProperty("postTitle")
public String getPostTitle() {
return postTitle;
}
#JsonProperty("postTitle")
public void setPostTitle(String postTitle) {
this.postTitle = postTitle;
}
#JsonProperty("created")
public Object getCreated() {
return created;
}
#JsonProperty("created")
public void setCreated(Object created) {
this.created = created;
}
#JsonProperty("tagList")
public String[] getTagList() {
return tagList;
}
#JsonProperty("tagList")
public void setTagList(String[] tagList) {
this.tagList = tagList;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
I'm trying to update an object via REST services using Spring MVC + Swagger Annotations.
The method is something like this:
#ApiOperation(value = "Modifies the entity")
#RequestMapping(value = "/entity", method = RequestMethod.PUT, headers = "Accept=application/json")
#APIMonitor
#ResponseBody
public PubTagger saveEntityDetails(
HttpServletResponse response,
ModelMap model,
#RequestBody final EntityClass entityInfo
)
throws Exception {
...
}
The entity definition is:
{
"id": "long",
"description": "string",
"name": "string",
"properties": [
{
"name": "string",
"value": "string"
}
]
}
It gives me an error
The request sent by the client was syntactically incorrect ()
But it only happens when I fill the objects inside the Properties field. If I leave it empty it succeeds. So I deduce there's something wrong in Spring MVC with nested objects inside lists.
Is there anything I'm missing here? Do I have to specify anything in the model to make it work?
Edit: Posting Entity class
public class Entity {
private Long id;
private String name;
private String description;
private List<Property> properties = new ArrayList<>();
public void setId(final Long id) {
this.id = id;
}
public Entity() {
super();
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(final String description) {
this.description = description;
}
public List<Property> getProperties() {
return properties;
}
public void setProperties(List<Property> properties) {
this.properties = properties;
}
}
Thanks, I found the error.
It was the class Property that only had the Parametrized constructor, without default constructor which made unable to marshall the JSON requestBody into an object.