How to apply Single annotation on multiple variables? - java

I am rookie in Java Annotation and have been searching for applying single annotation on multiple variable simultaneously.
Code:
#Document(collection = "users")
public class User {
private ObjectId id;
#NotNull
private String email;
private String imageURL;
private String authToken;
private Date createdDate;
private Date updateDate;
private boolean isActivated;
private int credits;
.....getter/Setter Method
I want to apply #NotNull property on email, imageURL and authToken too. I can do it by writing #NotNull to each variable but not preferring. How to do it?

#NotNull annotation can be applied at element not at group of elements.
JavaDoc: The annotated element must not be null. Accepts any type.
If you really want to get away with boiler plate code, you can use frameworks like Lombok which can help you to certain extent.
Link : http://projectlombok.org/features/Data.html
OR you can use reflection to validate all the method.
for (Field f : obj.getClass().getDeclaredFields()) {
f.setAccessible(true); // optional
if (f.get(obj) == null) {
f.set(obj, getDefaultValueForType(f.getType()));
// OR throw error
}
}

Java does not support multiple annotation of this type. But you can write something like this
Create a class with annotated field.
Create setters and getters to access the field.
Create all your name,email field as instance of this class.
This way fields will implicitly annotated as NotNull.
public class NotNullString {
#NotNull
String str;
public void set(String str)
{
this.str = str;
}
public String get()
{
return this.str;
}
}
NotNullString name;
NotNullString email;

Related

QUARKUS - MicroProfile REST Client: add a custom, not mapped field

I am following this article https://quarkus.io/guides/rest-client to build a REST Client to parse the output from the restcountries.eu service.
Here the class holding the model:
public class Country {
public String name;
public String alpha2Code;
public String capital;
public List<Currency> currencies;
public static class Currency {
public String code;
public String name;
public String symbol;
}
}
Now, suppose I would like to add a custom fields such as timestamp, to record the instant when this object has been created. I imagine, I would go ahead and add another field like below:
public class Country {
public String name;
public String alpha2Code;
public String capital;
public List<Currency> currencies;
public Instant timestamp; //<--------- added attribute
[....]
My question is: how do I tell the client to populate that field? Normally, I would have done it in the constructor. However, I could not find docs that explain this part.
Thanks for your help
Simone
You can actually do this in the default constructor. Frameworks like JSONB or Jackson expect POJOs to have a default constructor. They will call it when they create an instance of Country.
Use the #JsonbTransient or #JsonIgnore annotations to mark that attribute of your POJO as ignorable in order to avoid the unmarshaller complaining about attributes that cannot be found in the response.
#Data
public class Country {
private String name;
private String alpha2Code;
private String capital;
private List<Currency> currencies;
#JsonbTransient // if you're using JSONB (default in Quarkus)
#JsonIgnore // if you're using Jackson
private Instant timestamp;
public Country() {
this.timestamp = Instant.now();
}
PS The #Data annotation is something you should consider using. Encapsulation is not a bad thing but creating getters/setters is tedious. But Project Lombok certainly helps here.

Create #ToLowerCase annotation to convert String value to lower case

We have SpringBoot application.
For our pojo's we want to create a custom #ToLowerCase annotation which converts the field variable value to lower case.
Eg:
#Data
Employee {
private String name;
#ToLowerCase
private String emailId;
private String gender;
private String phoneNumber;
}
So my custom #ToLowerCase annotation should convert emailId to lower case.
We want to use this annotation on all kind of Pojos, whether it is rest request pojo or JPA entity pojo.
I have gone through posts on many forums but didn't get any appropriate solution for same.
Is it possible to create such annotation in Spring Boot? If yes then how?
Kindly help
Thanks
Create a custom converter: ToLowerCaseConverter.
public class ToLowerCaseConverter extends StdConverter<String, String> {
#Override
public String convert(String value) {
if (value == null){
return null;
}
return value.toLowerCase();
}
}
After create a new annotation: ToLowerCase. It works for both incoming and outgoing Strings (#JsonDeserialize/#JsonSerialize).
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotationsInside
#JsonSerialize(converter = ToLowerCaseConverter.class)
#JsonDeserialize(converter = ToLowerCaseConverter.class)
public #interface ToLowerCase {
}
Finally, your example will work as intended:
#Data
Employee {
#ToLowerCase
private String emailId;
}

Jackson: remove some values from json and keep some null values

I have a model like this:
public class Employee {
#JsonProperty("emplyee_id")
private Integer id;
#JsonProperty("emplyee_first_name")
private String firstName;
#JsonProperty("emplyee_last_name")
private String lastName;
#JsonProperty("emplyee_address")
private String address;
#JsonProperty("emplyee_age")
private Byte age;
#JsonProperty("emplyee_level")
private Byte level;
//getters and setters
}
now I need to create two JSONs using this (only) model.
the first one must like this for example:
{
"employee_id":101,
"employee_first_name":"Alex",
"employee_last_name":"Light",
"employee_age":null,
"employee_address":null
}
and the second one must like this for example:
{
"employee_id":101,
"employee_level":5
}
by the way, I already tested #JsonIgnore and #JsonInclude(JsonInclude.Include.NON_NULL).
the problem of the first one (as much as I know) is, those fields can't be included in other JSONs (for example if level get this annotation, it won't be included in the second JSON)
and the problem of the second one is, null values can't be included in JSON.
so can I keep null values and prevent some other property to be included in JSON without creating extra models? if the answer is yes, so how can I do it? if it's not I really appreciate if anyone gives me the best solution for this state.
thanks very much.
it could be useful for you using #JsonView annotation
public class Views {
public static class Public {
}
public static class Base {
}
}
public class Employee {
#JsonProperty("emplyee_id")
#JsonView({View.Public.class,View.Base.class})
private Integer id;
#JsonProperty("emplyee_first_name")
#JsonView(View.Public.class)
private String firstName;
#JsonProperty("emplyee_last_name")
#JsonView(View.Public.class)
private String lastName;
#JsonProperty("emplyee_address")
private String address;
#JsonProperty("emplyee_age")
private Byte age;
#JsonProperty("emplyee_level")
#JsonView(View.Base.class)
private Byte level;
//getters and setters
}
in your json response add #JsonView(Public/Base.class) it will return based on jsonview annotations
//requestmapping
#JsonView(View.Public.class)
public ResponseEntity<Employee> getEmployeeWithPublicView(){
//do something
}
response:
{
"employee_id":101,
"employee_first_name":"Alex",
"employee_last_name":"Light",
"employee_age":null,
"employee_address":null
}
for the second one
//requestmapping
#JsonView(View.Base.class)
public ResponseEntity<Employee> getEmployeeWithBaseView(){
//do something
}
response
{
"employee_id":101,
"employee_level":5
}

Asymmetric serialization and deserialization using Jackson

I am using Jackson to serialize and deserialize data for a RESTful API. I'd like to have a REST resource (/comments) that allows to POST comments as well as to GET a list of comments.
Here's a (simplified) example of what gets posted to /comments.
{"text":"Text","author":"Paul","email":"paul#example.org"}
Here's what the result of GET /comments should look like:
[{"text":"Text","author":"Paul","emailHash":"76w0kjKP9HpsdhBjx895Sg=="}]
Since email addresses shouldn't be visible to anyone, I decided to return only a MD5 hash of the email addresses in the response.
I have created a simple POJO class Comment that has fields with getters and setters for text, author, email, and emailHash.
Now, when I serialize the result, what I get is the following:
[{"text":"Text","author":"Paul","email":null,"emailHash":"76w0kjKP9HpsdhBjx895Sg=="}]
But I really don't like email to be returned as null here. It rather shouldn't be included at all.
Using the annotation #JsonIgnore on that field will also ignore it on deserialization. Do I have to create two classes, say CreationComment and ResultComment with a super-class Comment that shares common fields or is there a way that avoids creating additional classes?
You don't have to create 2 classes at all. With Jackson you have full control of the behavior of a property during serialization and deserialization using annotations, with #JsonIgnorein the getter you prevent the property from being serialized in your Json response and using #JsonProperty annotation in the setter the property will be set during deserialization. The code will look like this:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Comment {
private String author;
private String email;
#JsonIgnore
public String getEmail() {
return email;
}
#JsonProperty
public void setEmail(String email) {
this.email = email;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
Comment comment = new Comment();
comment.setAuthor("anAuthor");
comment.setEmail("email#example.com");
try {
System.out.println(objectMapper.writeValueAsString(comment));
String json = "{\"author\":\"anAuthor\",\"email\":\"another#email.com\"}";
Comment fromJson = objectMapper.readValue(json, Comment.class);
System.out.println("Result from Json: author= " + fromJson.getAuthor() + ", email= " + fromJson.getEmail());
} catch (Exception e) {
e.printStackTrace();
}
}
}
The output after running the main() method to test the solution:
{"author":"anAuthor"}
Result from Json: author= anAuthor, email= another#email.com
Hope it helps,
Jose Luis
You can put #JsonIgnore on getEmail to prevent it from being serialized to JSON and use #JsonCreator to indicate to Jackson a constructor to use for deserialization. The constructor would then only accept an email property and would hash and assign to your emailHash field.
You can put a #JsonInclude annotation on your Comment class to prevent serialization of null fields too.
Your class would probably end up looking something like this:
#JsonInclude(Include.NON_NULL)
public class Comment {
private final String email;
private final String emailHash;
#JsonCreator
public Comment(#JsonProperty("email") String email) {
this.email = email;
this.emailHash = MD5.hash(email);
}
#JsonIgnore
public String getEmail() {
return email;
}
public String getEmailHash() {
return emailHash;
}
}

Jackson ignore serializing field depending on value

I know it's possible to ignore fields if they are null or if they are empty, but is it possible to ignore a field, for example if it is a String, and contains a certain substring?
This is possible if you e.g. use a combination of #JsonIgnore and a Converter.
If you assume the following Person POJO:
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Person {
private final String email;
private final String name;
public Person(final String name, final String email) {
this.name = name;
this.email = email;
}
// Will use special conversion before serializing
#JsonSerialize(converter = EmailConverter.class)
public String getEmail() {
return email;
}
// Will simply use default serialization
public String getName() {
return name;
}
}
In the POJO you define that only non-empty values should be included. Furthermore, it is declared that a specific converter is to be used for the email property. The converter can be defined like this:
public class EmailConverter extends StdConverter<String, String> {
#Override
public String convert(final String value) {
return Optional.ofNullable(value)
.filter(email -> email.length() > 0)
.filter(email -> email.contains("#"))
.orElse(null);
}
}
Note that the converter uses Optional which is a java-8 feature but any validation code will do just fine. When null is returned it is simply skipped since it was declared that way in the Person class.
For more info, check out the JavaDocs for Converter and #JsonSerialize.

Categories