Jackson ignore serializing field depending on value - java

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.

Related

JPA retrieve enum with white space

I have a spring boot application and I have defined some POJO classess to specify the data model. In a table I want to store enum columns but I have an enum data type and some of the enums contains spaces.
enum DataEnum {
Workload("Workload"),
ReleaseContent("Release content"),
"VerificationProc"("Verification proc")
private String name = "";
DataEnum (final String name) {
this.name = name;
}
public String getName() {
return name;
}
public DataEnum fromString(String value) {
return DataEnum.valueOf(value);
}
#Override
public String toString() {
return name;
}
}
I have a POJO entity class as below:
#Entity
#IdClass(FieldId.class)
public class Field {
#Id
private String id;
#Id
#Enumerated(EnumType.STRING)
private DataEnum sheet;
...
}
When I try to retrieve data from the database I get:
java.lang.IllegalArgumentException: Unknown name value [Release
content] for enum class [data.util.DataEnum]
JPA cannot map your string to enum because it cannot find any enum with that name exists.
Internally, the #Enumerated use the method Enum.valueOf to convert the DB String to enum constant. The DB String required to be exactly the same to the enum constant identifier.
That is, if you want to store DataEnum.ReleaseContent, you have to store it as ReleaseContent not Release content.
In your case, if you want to store value different than your enum identifier, you can declare a custom converter:
#Converter
public class DataEnumConverter implements AttributeConverter<DataEnum, String> {
#Override
public String convertToDatabaseColumn(DataEnum enum) {
// Convert your enum to DB value
}
#Override
public DataEnum convertToEntityAttribute(String dbValue) {
// Convert String to your enum
}
}
You can see My Answer for an example how to convert String to enum
And in your entity:
#Column
#Convert(converter = DataEnumConverter.class)
private DataEnum sheet;

Change JSON property name on derived classes using Jackson library

I am using Spring to develop new REST API, I have BaseResponse class which acts as base responses for all response. this class contains attribute String requestUuid; at some cases this requestUuid must be serialized with attribute name requestUuid , on other cases it must be seriliazed as request_uuid, i know i can use #JsonProperty as a field level annotation, but it will affect all responses. is there is any way to override attribute name specifically for each one of the derived classes.
You can use the #JsonProperty on the method level instead. That way, you can override the field's getter method in the subclass and annotate that.
For example:
class BaseResponse {
private String requestUuid;
public getRequestUuid() {
return requestUuid;
}
}
class OtherResponse extends BaseResponse {
#Override
#JsonProperty("request_uuid")
public getRequestUuid() {
return super.getRequestUuid();
}
}
You can send the field twice with different key names.
#JsonAnyGetter
public Map<String, Object> otherFields() {
Map<String, Object> otherFields = new HashMap<>();
otherFields.put("requestUuid", this.requestUuid);
otherFields.put("request_uuid", this.requestUuid);
return otherFields;
}
Also, ignore your actual field:
#JsonIgnore
private String requestUuid;
Expanding on #JoshA response, another alternative is to define a constructor and annotate it. This leads to a more concise code by avoiding the need to override the getter methods in derived classes.
class BaseResponse {
private String firstName;
private String lastName;
public BaseResponse(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public getFirstName() {
return firstName;
}
public getLastName() {
return lastName;
}
}
class OtherResponse extends BaseResponse {
public OtherResponse(#JsonProperty("given_name") String firstName, #JsonProperty("family_name") String lastName) {
super(firstName, lastName);
}
}
NO, its not possible, what is possible you can make new class for different type of requests.

Jackson map special character to null

How to configure Jackson json deserializer such that special characters say "##", "#null", "-" to null
For example:
Say my json is
{ "userId":"kp","fname":"k","lname":"p","mname":"##", rndNum:"-"}
And Java class is
class User{
private String userId;
private String fname;
private String lname;
private String mname;
private Integer rndNum;
//getters/ and setters
}
I would expect output to be
user.getUserId() = kp
user.getMname() = null
user.getRndNum() = null
The simplest way would be to use a constructor and a validator for that. e.g.
public static String validate(String name) {
if (name != null && !name.isEmpty() && (name.charAt(0) == '#' || name.charAt(0) == '-')) {
return null;
}
return name;
}
class User {
User(String userId, String fname, String lname, String mname) {
this.userId = validate(userId);
this.fname = validate(fname);
this.lname = validate(lname);
this.mname = validate(mname);
}
I also think this properly separates functionality. Jackson code and annotations should handle deserialization of whatever happens to be in the JSON. Your class should know if values used for initialization is valid regardless where they come from.
There are two ways to do this depending on how advanced solution you need:
There is an easy way if you are willing to register your types one-by-one. In this solution you are providing the deserialization code from raw Jackson objects to your desired output.
Or you can go with the more powerful solution which overrides the default Serializer by using a custom provider to cover all types. Here you are defining how Strings should be deserialized in general.

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;
}
}

How to apply Single annotation on multiple variables?

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;

Categories