How to use Jackson to conditionally serialise the fields of a POJO - java

I have a java class
public class CategoryItem implements Serializable {
private Long id;
private String name;
private Manager manager;
}
In one case,I need to convert all the fields to json.
on the other case,I only need 'id'and 'name'
How can I do?
Give me some tips.Thanks

Annotate your POJO id and name attributes with #JsonProperty and manager with #JsonIgnore
When you want just id and name, use a default ObjectMapper.
When you want all fields, use a custom ObjectMapper per this question/answer.

There are many ways to do this:
set unwanted field to null, and use #JsonInclude(Include.NON_NULL) annotation at class level.
supply SimpleBeanPropertyFilter, while using ObjectMapper and use annotation #JsonFilter(<filter_name>) at class level.
use a custom-serializer.

Related

How to avoid adding #Valid on each and every inner-class fields during the hibernate-validator?

I am currently developing an application within that I am adding a few validations on an inner class such as #NotNull, #Min, #Max, etc.
To make the validations work I need to add the #Valid on each and every field which is making use of the inner class. Is there a way to avoid adding the #Valid on each and every object rather add some annotations on the Class so it can be applicable to all the fields within that class?
I am currently using the following library to achieve the validations:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
I tried to add the #Validated on the class but seems like this annotation is not available in this maven dependency. Can someone please let me know what I need to change?
Following is a simple example that is working but I would like to remove #Valid that I have to add on each field. If I do not add #Valid then those inner classes won't be validated.
public class Book {
#NotNull(message="Book ID cannot be NULL")
private int bookId;
#Valid
private List<Author> author;
#Valid
private List<Publication> author;
}
public class Author {
#NotNull(message="Author ID cannot be NULL")
private int authorID;
#NotNull(message="Author Name cannot be NULL")
private String name;
}
public class Publication {
#NotNull(message="Publication ID cannot be NULL")
private int authorID;
#NotNull(message="Publication Name cannot be NULL")
private String name;
}
There is no way to do what you want to do, except if you write your own Quarkus extension that will add the annotations at build time.
It will be some rather involved work, though, as you will need to add some bytecode transformation to add the annotations where you want them.
Also, you should add the #Valid annotations inside the List e.g. List<#Valid Publication> rather than at the field level. It's more optimized this way.

Using Data annotation on Java DTO class

I have confusion in using Data annotation to DTO class. Here is my sample class.
#Data
public class MyClass {
private Long id;
private String name;
}
I've read online that using Data annotation specifically on DTO class will allow the values of the fields to be changed by use of the generated setters.
Should I remove the lombok Data annotation? And implement the getters and setters manually.
Thanks :)
I would avoid #Data for DTOs since it has too many pitfalls. First of all as you mentioned it is mutable which you don't really want for a DTO. And despite it being mutable, it implements equals() and hashCode() which is only asking for trouble.
You can use #Value for an immutable DTO. For an incoming DTO you may need to add lombok.anyConstructor.addConstructorProperties to your lombok.config, which will allow libraries like jackson to deserialize to your POJO without a default constructor.
The annotation #Data comes from the Project Lombok which is designed to use reflection via annotations mostly. This annotation assures generation of all the setters, getters, a constructor with all the required arguments and overridden Object::toString, Object::equals and Object::hashCode methods.
Briefly said, this annotation "completes" a simple POJO object and generates all the boilerplate without a need to use IDE.
They named the annotation #Data because they support the idea of the understanding objects as data containers only.
As far as I understand, the generation happens only for the missing getters/setters (let's speak about them for brevity). The generated getters/setters are in their pure form as you know:
public int getId() { return this.id; }
public void setId(int id) { this.id = id; }
You can use more verbose setter/getter performing validation or anything similar which override the generated ones. You can both use #Data annotation and write your ones manually.
DTO is used to transmit data information, some information is actually we do not want users to be able to change or access, such as the user password, we do not want to pass to the front end when the user can see the encrypted password, or we do not want users to modify the password while changing their information, and what works in this serialization process is setter and getter, and data annotations that automatically generate getters and setters for all fields.
For example
#Data
class User{
private String userName;
private String pwd;
}
This class, will have all setter and getter. When you trans to web, you will see
{userName: "123", pwd: "xxx"}
This is terrible.
But if you use DTO
class User{
private String userName;
private String pwd;
public String getUserName(){
return userName;
}
}
They only see
{userName: "123"}
By default the #Data lombok annotation will generate setters and getters for all fields in the class.
If you want an immutable data transfer object, annotate it as #Value instead.
If you want a mixure of some immmutable values and some mutable values in your MyClass type, for instance you might want the id field to be immutable and the rest mutable, you would use the #Setter annotation on the field you want to be immutable, specifying an AccessLevel of NONE. For instance:
#Data
public class MyClass {
#Setter(AccessLevel.NONE)
private Long id;
private String name;
}
This will generate a getter but no setter for the id, and a getter and setter for the name.

Restrict JSON attributes for #RequestBody

This may be a simple task, but I couldn't find a way to do it. Basically, I need to disallow some parameters at the time of using #RequestBody annotation in my controller.
Here is my model:
#Data
public class MyModel {
private int id;
private String name;
}
What I want to do is at the time of response, I want both of the properties to be serialized to JSON, but at the time of create or update, I prefer not to receive id as part of #RequestBody deserialization.
Right now, if I pass id in the JSON body, Spring initializes a MyModel object with its id set to the passed value.
Reason? The ID cannot be generated until the model is created, so the app shouldn't allow the ID to be set. On update, the ID needs to be passed in the URL itself e.g. (PUT /mymodels/43). This helps following the REST principles appropriately.
So, is there any way to achieve this functionality?
Update 1:
Right now, I am stuck with using a request wrapper. I created a new class MyModelRequestWrapper with only name as its property, and have used it with the #RequestBody annotation.
How you do this depends on what version of Jackson you are using. It's basically possible by a combination of the annotations #JsonIgnore and #JsonProperty on relevant fields/getters/setters.
Have a look at the answers here: Only using #JsonIgnore during serialization, but not deserialization

How to impose common constraint on class with javax.validation library?

I am using Bean Validation constraints to validate class instances at run time. I have many DTOs where each has multiple fields often with a common constraint. I want to add a constraint to the class so that it applies to all the properties of the class. (As lombok #NotNull constraint).
e.g
class Person {
#NotNull
private String name;
#NotNull
private String address;
#NotNULL
private String contact;
}
I want to make it something like this.
#NotNull
class Person {
private String name;
private String address;
private String contact
}
You cannot do this with plain Bean Validation. So just adding #NotNull to the class won't work.
One potential approach could be to utilize XML configuration. The idea would be to have your own annotation MyNotNull. Your framework would need to scan for these annotations and build the programmatic configuration for the class in question. This could for example be done via a annotation processor during compile time. Once you have the constraint mapping XML files add them to your jar and reference them in validation.xml. That's just a basic idea. Personally, I am not sure whether it is worth the effort.

Validating JSON inside a POJO

What is the best/preferred way to validate JSON using annotations inside a POJO?
I would like to be able to distinguish between optional and required fields of a POJO.
I would like to be able to provide default values for required fields of a POJO.
Example:
#JsonTypeInfo(use=Id.NAME, include = As.WRAPPER_OBJECT)
#JsonTypeName("Foo")
public class MyClass{
#JsonProperty
private String someOptionalField;
#JsonProperty
private String someRequiredField;
#JsonProperty
private String someRequiredFieldThatIsNotNull;
#JsonProperty
private int someRequiredFieldThatIsGreaterThanZero;
// etc...
}
A possible approach is to deserialize JSON into an object and validate an object with validation API #MattBall linked. The advantage is that this logic is being decoupled from storage logic and you are free to change your storage logic with no need to reimplement validation.
If you want to validate JSON, you might want to have a look at JSON schema.

Categories