Jackson's #JsonView annotation does not work - java

I annotated class User with #JsonView and when it returned I see all fields even than that not contains in view class. Here is my class
#Entity
#Table(name = "users")
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
private Long userID;
#JsonView(View.Summary.class)
#Column(name="email")
private String email;
#JsonView(View.Summary.class)
#Column(name="user_name")
private String firstName;
#JsonView(View.Summary.class)
#Column(name="user_last_name")
private String lastName;
#JsonView(View.Summary.class)
#Column(name="phone")
private String phone;
#JsonView(View.Summary.class)
#Column(name="origin")
private String address;
#JsonView(View.Summary.class)
#Column(name="birth_date")
private Long birthDate;
#JsonView(View.Summary.class)
#Column(name="gender")
private Long gender;
#JsonView(View.Summary.class)
#Column(name="about_me")
private String aboutMe;
#JsonView(View.SummaryWithPhoto.class)
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="photo")
private Photo avatar;
#JsonView(View.SummaryWithSession.class)
#Transient
private UserSession session;
//getters and setters
Here is my View class
public class View {
public interface Summary {}
public interface SummaryWithPhoto extends Summary {}
public interface SummaryWithSession extends SummaryWithPhoto {}
}
SO then I request get method with #JsonView(View.SummaryWithPhoto.class) annotation I always get userID field but shouldn't. Here is endpoint code
#JsonView(View.SummaryWithPhoto.class)
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<User> getUser(#RequestHeader(value="Access-key") String accessKey,
#RequestHeader(value="Secret-key") String secretKey)

I've spend a some debugging time with the same issue.
Results are:
all fields are included by default if you do not change this behavior (see BeanSerializerFactory.processViews). To change default do:
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
fields, marked by #JsonView omitted in result if controller method annotated with OTHER #JsonView (see FilteredBeanPropertyWriter.serializeAsField)
So for your use-case do not change default settings, annotate Long userID by #JsonView and getUser by any other (not the same) View.
Code com\fasterxml\jackson\core\jackson-databind\2.8.4\jackson-databind-2.8.4-sources.jar!\com\fasterxml\jackson\databind\MapperFeature.java
* Feature is enabled by default.
*/
DEFAULT_VIEW_INCLUSION(true)
is contradicted of blog https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring, so I have to look at code closer.

Related

Template and Styling not working when calling a view using #RequestParam annotation

I am extracting single employee to create employee dashboard page using #RequestParam annotation. I am able to get object data and able to log it and able to pass it to html page, but when view is called it is unable to render any styling. All content displays as plain html text.
Below is my model
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String firstname;
private String middlename;
private String lastname;
private String familyname;
private String nationalityid;
private String gender;
private String fatherfirstname;
private String fatherlastname;
#ManyToOne
#JoinColumn(name="jobtitleid", insertable=false, updatable=false)
private JobTitle jobtitle;
private Integer jobtitleid;
#ManyToOne
#JoinColumn(name="departmentid", insertable=false, updatable=false)
private Department department;
private Integer departmentid;
private String familyStatus;
#DateTimeFormat(pattern = "yyyy-MM-dd")
private Date hireDate;
#DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dateofbirth;
private String address;
}
This is method which is passing single employee data to view employee**
#RequestMapping(value="/employees/getEmployeeById", method= {RequestMethod.GET})
public String getEmpById(#RequestParam Integer id, Model model) {
Employee employee = employeeService.findById(id).get();
model.addAttribute(employee);
System.out.println(employee);
return "employee";
}
Data is getting displayed in view page ("employee")
Also it is being displayed in log but when the link http://localhost:8080/employees/getEmployeeById/?id=1 navigates to view no styling works. Bootstrap also does not work. I am display data as plain html but styling doesn't work.
image showing data in table
Use Annotation #Controller on the above of #RequestMapping

Spring #validated is not cascaded to fields

I need to use groups so that I can use the same DTO for creating and patching; however, the requirements for these operations are obviously different. Therefore, I decided to use Spirng's #Validated instead of #Valid along with groups. Here is the simplified version of my code:
class PersonDto{
#NotBlank(groups = CreateConstraint.class)
private String phoneNumber;
#NotNull(groups = CreateConstraint.class)
#Valid
private AddressDto address;
}
and in the AddressDto, I have used the default group:
#Getter
#Setter
public class AddressDto {
#NotBlank
private String line1;
private String line2;
private String city;
#NotNull
private Province province;
#NotBlank
private String postalCode;
}
And the controller:
#PatchMapping("/patch")
public void patchPerson(#RequestBody #Validated PersonDto dto) {
personService.patchPerson(dto);}
In the controller here I am using the default group as well. Now, unless I explicitly add #Valid to the AddressDto declaration in PersonDto, the validation is not done for the AddressDto. why is it that #Validated is not cascaded to the fields inside the PersonDto? Thanks.
Edit:
Just to be clear #Valid does validate recursively. wondering why #Validated not...
You have to add the validation group to the cascaded fields as well.
For example, check the line1 field for the CreateConstraint group.
Also, you have to specify the validation group in the controller.
#Getter
#Setter
public class AddressDto {
#NotBlank(groups = CreateConstraint.class)
private String line1;
private String line2;
private String city;
#NotNull()
private Province province;
#NotBlank()
private String postalCode;
}
#PatchMapping("/patch")
public void patchPerson(#RequestBody #Validated({CreateConstraint.class})PersonDto dto) {
personService.patchPerson(dto);
}

#GetMapping used to retrive item gives a responce of infinite loop of foreignkey object

i am new in spring boot and i could not find solution for this for a day now.
#GetMapping used to retrive item gives a responce of infinite loop of foreignkey object "user".
why am i getting this infinite loop?
how to fix it?
user object in infinite loop(the problem)
result that i want
item entity
#Entity
public class Item{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long ItemId;
#ManyToOne
#JoinColumn(name = "owner_id")
private User user;
private String ItemName;
// #Column(columnDefinition="text")
private String Description;
private double Price;
private int AvailableQuantity;
private double shippingWeight;
// #Transient
// private MultipartFile Picture;
#Enumerated(value = EnumType.STRING)
private Category category;
#OneToMany(mappedBy = "item")
#JsonIgnore
private List<CartItem> CartItemList;
}
user entity
#Entity
#Table(name = "Utilisateur")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idU;
private String username;
private String password;
private String firstname;
private String lastname;
private String gender;
private Long phone;
private String adress;
#Temporal(TemporalType.DATE)
private Date dateofbirth;
private int rating;
private String email;
public Role role;
private Integer status;
#OneToMany(mappedBy = "user")
private List<Item> ItemList;
}
item service
#Service
public class ItemService implements ItemServiceInterface{
#Autowired
ItemRepository itemrepository;
public Optional<Item> getItemById(long id){
return itemrepository.findById(id);
}
}
item controller
#RestController
public class ItemControl {
#Autowired
ItemServiceInterface itemservice;
#GetMapping("/getitem/{id}")
public Optional<Item> getitembyid(#PathVariable Long id) {
return itemservice.getItemById(id);
}
}
You can use combination of #JsonManagedReference and #JsonBackReference to discourage Jackson from infinite serialization.
#Entity
#Table(name = "Utilisateur")
public class User {
// omitted
#JsonManagedReference
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Item> ItemList;
}
#Entity
public class Item{
// omitted
#JsonBackReference
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "owner_id")
private User user;
}
More details could be found here https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
You can make use of lazy loading to cut the dependency loop between user and item. However, following that approach might potentially affect other parts of your projects because other codes might use the entity with an assumption that item list in user entity is already eager fetched.
A better way is not return the entity object directly to the REST response. You can define a data model for the rest response and convert the entity to that model in your service class. This way, you can completely control what to return and not to.
Another approach if you still want to use the entity as response: https://www.baeldung.com/spring-data-jpa-named-entity-graphs. This way, you can define when to use the lazy load with each specific query.

Validation a DTO with two entity

I have a DTO with two entity. How can I validate these entities?
What annotation should I use?
I use rest api, JSON, spring boot.
I know how to validate one entity. But I don't know what to do with DTO.
#PostMapping
public ResponseEntity<?> create(#Valid #RequestBody DTOClient client) {
....
return responseEntity;
}
public class DTOClient{
//What I should use here to validate these entities?
private Client client;
private Skill skill;
}
#Entity
public class Client{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String first_name;
private String last_name;
}
#Entity
public class Skill{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private int year;
}
Use javax.validation for the fields which you want to validate. Below code is an example to validate first_name in client object should not null or blank.
#PostMapping
public ResponseEntity<?> create(#Valid #RequestBody DTOClient client) {
....
return responseEntity;
}
public class DTOClient{
//What I should use here to validate these entities?
#Valid
#NotNull(message="client should not null")
private Client client;
private Skill skill;
}
#Entity
public class Client{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotBlank(message="first name of client should not be null or blank")
private String first_name;
private String last_name;
}
#Entity
public class Skill{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private int year;
}
In short, you need use #Valid for Bean, like controller methods' params and the fields which not primary. And Constraint annotations for the fields which need validate.

Spring Boot - how to validate nested enteties

So lets say I have User object like this
#Entity
public class User {
#Id
#GeneratedValue
private long id;
private String name;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "address", referencedColumnName = "id")
private Address address;
}
#Entity
public class Address {
#Id
#GeneratedValue
private long id;
private String city;
private String country;
}
Now I don't want to write validation annotations in entities. What I would like to do is validate User in #RestController like this
#RestController
public class InvoiceController {
#RequestMapping(value="/users/add", method = RequestMethod.POST)
public Invoice addInvoice(#Validated #RequestBody ValidUser user) {
... do stuff
}
}
The validation annotations would be in ValidUser being like this.
public class ValidUser extends User {
#NotNull
private String name;
#Valid
private Address address;
}
public class ValidAddress extends Address{
#NotNull
private String city;
#NotNull
private String country;
}
The validation works when I remove the address field from the ValidUser but not when it is there. How can I make address validation also work?

Categories