I have a enum like as:
public enum Age {
THREE("3"),
FIVE("5");
private final String value;
Age(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
and a User class like:
public class User {
#NotNull
String name;
Age age;
public User() {
}
public User(#NotNull String name, Age age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getAge() {
return age;
}
public void setAge(Age age) {
this.age = age;
}
}
and a RestController like:
#ExceptionHandler(HttpMessageNotReadableException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<User> exceptionHandling(HttpMessageNotReadableException exception,
HandlerMethod handlerMethod, WebRequest webRequest) {
logger.error("error:" + exception.getLocalizedMessage());
EnumValidationException ex = (EnumValidationException) exception.getMostSpecificCause();
User user = new User();
user.setName(""); // I want set user's input
user.setAge(Age.FIVE);
return ResponseEntity.ok(user);
}
#ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<User> exceptionHandling2(MethodArgumentNotValidException exception) {
logger.error("error:" + exception.getLocalizedMessage());
User user = new User();
user.setName(""); // I want set user's input
user.setAge(Age.FIVE);
return ResponseEntity.ok(user);
}
#PostMapping("/user2")
public String setUser2(#Valid #RequestBody User user) {
return "ok";
}
I sed a JSON like:
{
"name":"Name",
"age":"11"
}
now how I handle its HttpMessageNotReadableException exceptions that return Name in name field in response?
should I change enum to static final String?
can I write customized Annotaion? how handle its getValue()?
Note: I use Hibernate.
If I understood your question, you want to print the value of Age, not the
name: in that case, you can try overriding toString() in Age:
#Override
public String toString() {
return value;
}
If you want to use getValue(), you should read the documentation of Spring Boot and especially its underlying JSON API:
https://github.com/FasterXML/jackson
https://github.com/FasterXML/jackson-annotations
https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations
Related
I have the JSON looks like the following:
{
"name":"John",
"n_age":500
}
and I have a class Person:
public class Person {
private final String name;
private final int age;
#JsonCreator
public Person(#JsonProperty("name") String name) {
this.name = name;
this.age = 100;
}
public String getName() {
return name;
}
#JsonGetter("n_age")
public int getAge() {
return age;
}
}
I need to deserialize and serialize it, but when I'm trying to deserialize this JSON I get unexpected result.
public static void main(String... args) {
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue(args[0], Person.class);
System.out.println(person.getAge()); // I got 500, but I expect 100.
}
Why when I'm trying to deserialize it the #JsonGetter annotation is used for it?
How can I disable #JsonGetter annotation when I try to deserialize the JSON?
If #JsonGetter is used as is currently, it will map property n_age to field age. To citate the docs - It can be used as an alternative to more general JsonProperty annotation (which is the recommended choice in general case).
To fix this behaviour, you need to:
Tell jackson to ignore property n_age, otherwise you will get exception for unrecognized property not marked as ignorable - #JsonIgnoreProperties("n_age").
Tell jackson to allow getters for ignored properties(basically make it readonly) - #JsonIgnoreProperties(value = {"n_age"}, allowGetters = true)
In the end, Person should look like this:
#JsonIgnoreProperties(value = {"n_age"}, allowGetters = true)
public class Person {
private final String name;
private final int age;
#JsonCreator
public Person(#JsonProperty("name") String name) {
this.name = name;
this.age = 100;
}
public String getName() {
return name;
}
#JsonGetter("n_age")
public int getAge() {
return age;
}
#Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
I found the solution for fixing my issue, maybe it's a bad way, but it works for me as well. I'm ignoring the n_age property during deserialization and allowing getters during serialization.
Thanks a lot #Chaosfire for the help!
#JsonIgnoreProperties({"n_age"}, allowGetters = true)
public class Person {
private final String name;
private final int age;
#JsonCreator
public Person(#JsonProperty("name") String name) {
this.name = name;
this.age = 100;
}
public String getName() {
return name;
}
#JsonGetter("n_age")
public int getAge() {
return age;
}
}
I'm using Spring boot trying to obtain a JSON response with #RestController and #GetMapping and it does not come out with JSON on the local host. This is my code. Can anyone fix this?
#SpringBootApplication
#RestController
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#GetMapping
public List<Employee> hello () {
return List.of(
new Employee(
1L,
"Pedro",
"rt.pedrosantos#gmail.com",
LocalDate.of(1989, Month.JUNE, 21),
32
)
);
}
}
The Following is the "Employee" Class with setters and getters I created to go with it.
package com.example.employee;
import java.time.LocalDate;
public class Employee {
private Long id;
private String name;
private String email;
private LocalDate dob;
private Integer age;
public Employee() {
}
public Employee(Long id,
String name,
String email,
LocalDate dob,
Integer age) {
this.id = id;
this.name = name;
this.email = email;
this.dob = dob;
this.age = age;
}
public Employee(String name,
String email,
LocalDate dob,
Integer age) {
this.name = name;
this.email = email;
this.dob = dob;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public LocalDate getDob() {
return dob;
}
public void setDob(LocalDate dob) {
this.dob = dob;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
#Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", dob=" + dob +
", age=" + age +
'}';
}
}
class ends here. I'm not able to properly return the code to JSON and I'm not sure why. Can anyone explain?
Edit: It returns a Json. Check if your browser or rest client is properly configured.
Previous answer:
Refer to this: As you have annotated with #RestController there is no need to do explicit json conversion. Since you're not returning a POJO, and according to the code you've posted you don't have any getters, changing it to the following does the job:
#RequestMapping(value = "hello_world", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public List<StringResponse> hello() {
return List.of(new StringResponse("Hello"),new StringResponse("World"));
}
}
class StringResponse {
private String response;
public StringResponse(String s) {
this.response = s;
}
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
}
or use a library like Gson: There is a clean way to return string as json in a Spring Web API?
Example:
#RequestMapping(value = "hello_world", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public List<String> hello() {
Gson gson = new GsonBuilder().create();
Type type = new TypeToken<List<String>>() {}.getType();
String json = "[ \"Hello\", \"World\"]";
List<String> responseList = gson.fromJson(json, type);
return responseList;
}
More info: Spring MVC - How to return simple String as JSON in Rest Controller
How to return JSON data from spring Controller using #ResponseBody
Not sure what you're trying to do, but creating a model class with corresponding setters / getters should be the way.
I would like to ask your ideas about designing best validation approach for below requirements:
We have a User model class and depending on it is status we can update some specific fields. As you can see, changeability of the model class depends on its status field.
If the status of user is ACTIVE then all fields (name, surname,
password ....) can be updated.
If the status of user is INACTIVE only password can be updated.
If the status of user is BLOCKED then name and surname can be
updated.
If the status of user is DELETED then update operation is not
allowed for any field.
Obviously, it can be done simply by adding a UserValidator class and before setting values in setter methods I can call my UserValidator to check if the operation is allowed or not. However, it has a drawback (?): what will happen if there will be new field (let's say maritalStatus) and dev who adds that field forgets to call UserValidator before setting maritalStatus?
Other ways of solving this problem that I can think of:
Using custom annotations by extending CustomValidator. However, it
won't work as annotation cannot know the previous values of object.
I mean, the isValid() method of CustomValidator won't know if the name
field has changed or not (it was "John" and now dev wants to change it
to "Jack")
Proxy pattern could be useful but not sure if it is good idea to use
proxy for model objects.
I've seen that the decorator pattern can be used for this problem but I don't understand how. I think validating model class is beyond the responsibility of a decorator design.
public class User {
private Integer id;
private String name;
private String surname;
private String password;
private Status status;
// setters
}
public enum Status {
ACTIVE, DELETED, INACTIVE, BLOCKED
}
I suggest you to use intention-revealing interfaces instead of setter/getter
E.g.
public class User {
private Integer id;
private String name;
private String surname;
private String password;
private Status status;
void rename(String newName, String newSurname){
if(Status.INACTIVE.equals(status) || Status.DELETED.equals(status))
throws new Exception("You cannot rename a inactive or deleted user");
this.name = newName;
this.surname = newSurname;
}
.....
}
Here is the approach I will use. Would be very nice if you guys can also vote for that.
Basicly I will use Decorator pattern.
public interface User {
Integer getId();
void setId(Integer id);
String getName();
void setName(String name);
String getSurname();
void setSurname(String surname);
String getPassword();
void setPassword(String password);
Status getStatus();
void setStatus(Status status);
}
public class UserImpl implements User {
private Integer id;
private String name;
private String surname;
private String password;
private Status status;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}
public abstract class UserDecorator implements User{
private final User user;
public UserDecorator(User user) {
this.user = user;
}
public Integer getId() {
return user.getId();
}
public void setId(Integer id) {
user.setId(id);
}
public String getName() {
return user.getName();
}
public void setName(String name) {
user.setName(name);
}
public String getSurname() {
return user.getSurname();
}
public void setSurname(String surname) {
user.setSurname(surname);
}
public String getPassword() {
return user.getPassword();
}
public void setPassword(String password) {
user.setPassword(password);
}
public Status getStatus() {
return user.getStatus();
}
public void setStatus(Status status) {
user.setStatus(status);
}
}
InActive User:
public class InactiveUserImpl extends UserDecorator {
public InactiveUserImpl(User user) {
super(user);
}
// didn't override setPassword therefore that field can be updated when the status of user is Inactive
#Override
public void setId(Integer id) {
throw new IllegalArgumentException("Field can not be update when user is InActive");
}
#Override
public void setName(String name) {
throw new IllegalArgumentException("Field can not be update when user is InActive");
}
#Override
public void setSurname(String surname) {
throw new IllegalArgumentException("Field can not be update when user is InActive");
}
#Override
public void setStatus(Status status) {
throw new IllegalArgumentException("Field can not be update when user is InActive");
}
}
Active User:
public class ActiveUserImpl extends UserDecorator {
public ActiveUserImpl(User user) {
super(user);
}
// not overriding any method hence everything can be updated
}
Basically, whoever will ask for UserImpl I will return him wrapped version of UserImpl, For example,
class UserRepository{
public User getById(String id){
User user=db.getUserById(id);
// this can be done with Enum but it is out of our scope
if (user.getStatus().equals(INACTIVE))
return new InactiveUserImpl(user);
// ....
return null;
}
}
If you do :
User user= userRepository.getById(1)
then you will get user with the sets of allowed setters.
I am having two Spring Rest service create-employee and create-staff like as shown below
create-employee
#RequestMapping(value="/create-employee", method = RequestMethod.POST, consumes = "application/json")
public ResponseEntity<Void> createEmployee(final #RequestBody User user) {
try {
// employee createion logic
} catch (Exception exception) {
log.error("Exception in createEmployee:"+exception.getMessage());
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
}
}
create-staff
#RequestMapping(value="/create-staff", method = RequestMethod.POST, consumes = "application/json")
public ResponseEntity<Void> createStaff(final #RequestBody User user) {
try {
// staff creation logic
} catch (Exception exception) {
log.error("Exception in createStaff:"+exception.getMessage());
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
}
}
For both the services I am using a dto named User like as shown below:
public class User {
#JsonProperty("employeeName")
private String name;
#JsonProperty("age")
private Integer age;
#JsonProperty("managerName")
private String headName;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getHeadName() {
return headName;
}
public void setHeadName(String headName) {
this.headName = headName;
}
}
Now the issue is that for both create-employee and create-staff since I am using User.java as the request body. The posting json body looks like this
{
"employeeName" : "string",
"age" : "integer",
"managerName" : "string"
}
but actually what I want is that for create-staff service I would like to have the json body as below
{
"staffName" : "string",
"age" : "integer",
"managerName" : "string"
}
and create-staff service I would like to have the json body as below
{
"employeeName" : "string",
"age" : "integer",
"managerName" : "string"
}
But for both the services I need to use the same User.java dto but with different JsonProperty for the two services
Can anyone please hep me on this
Jackson also supports JsonAlias which might be helpful for you, just make sure you updated your jacskon mapper to version 2.9.1
public class User {
#JsonAlias({"employeeName","staffName"})
private String name;
#JsonProperty("age")
private Integer age;
#JsonProperty("managerName")
private String headName;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getHeadName() {
return headName;
}
public void setHeadName(String headName) {
this.headName = headName;
}
}
In my Spring web application:
#RequestMapping(value = NEW)
public String addProduct(#RequestParam String name, #RequestParam(required = false) String description,
#RequestParam String price, #RequestParam String company, ModelMap model,
#RequestParam(required = false) String volume, #RequestParam(required = false) String weight) {
try {
productManagementService.addNewProduct(name, description, company, price, volume, weight);
model.addAttribute("confirm", PRODUCT_ADDED);
return FORM_PAGE;
} catch (NumberFormatException e) {
logger.log(Level.SEVERE, INVALID_VALUE);
model.addAttribute("error", INVALID_VALUE);
return FORM_PAGE;
} catch (InvalidUserInputException e) {
logger.log(Level.SEVERE, e.getMessage());
model.addAttribute("error", e.getMessage());
return FORM_PAGE;
}
}
What are the possible ways to reduce/bind total number of arguments.
create Form Class i.e
class MyForm{
String name;
String price;
String description;
...
// Getters and setters included
}
and do like
#RequestMapping(value = NEW)
public String addProduct(#ModelAttribute MyForm myForm)
instantiation of MyForm and binding of request parameters to its properties and adding to ModelMap is done by spring behind the scenes.
Source: Spring Docs
An #ModelAttribute on a method argument indicates the argument should
be retrieved from the model. If not present in the model, the argument
should be instantiated first and then added to the model. Once present
in the model, the argument's fields should be populated from all
request parameters that have matching names. This is known as data
binding in Spring MVC, a very useful mechanism that saves you from
having to parse each form field individually.
Create a class, encapsulate all the attributes in that class and then accept that class object as your #ModelAttribute. Something like:
public class MyData {
private String name;
private String description;
private String price;
private String company;
private String volume;
private String weight;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getVolume() {
return volume;
}
public void setVolume(String volume) {
this.volume = volume;
}
public String getWeight() {
return weight;
}
public void setWeight(String weight) {
this.weight = weight;
}
}
And then modifying your addProduct method like this:
public String addProduct(#ModelAttribute MyData myData, ModelMap model)