I'm using lombok in my project and generation Setters and Getters using #Setters and #Getters annotations on top of POJO class. I'm trying to override setters method of a property but it's not working
I want to check if JSON property is Empty or Null i want to set default value in Setter method
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Accessors(chain = true)
#ToString
public class DefaultModel {
private String name;
#Setter(AccessLevel.NONE)private String age;
public void setAge(String age) {
if(age==null||age.trim().isEmpty()||age.equals("null")) {
this.age="10";
}else {
this.age=age;
}
}
}
Working scenarios:
{
"name":"some",
"age":null
}
{
"name":"some",
"age":"null"
}
{
"name":"some",
"age":" "
}
Failed Scenario :
{
"name":"some"
}
Output:
DefaultModel(name=some, age=null)
And i'm following this as reference also here, but no luck so far
Either you just hit a bug I've never seen or you're testing it wrong.
An annotation like
#Setter(AccessLevel.NONE) private String age;
on the field level indeed stops the setter from being generated. But given that you're defining a setter, you don't even need it. An explicit #Setter stops the generation, too.
I've just tried your example using Eclipse 4.7.3a and Lombok 1.18.0 and your (buggy) setter gets called. I've been using Lombok a lot over a few years and never encountered such a bug.
Most probably the problem is that your JSON deserializer does not use setters at all. I guess, you're testing something like
DefaultModel defaultModel = deserialize("{\"name\":\"some\"}", DefaultModel.class);
instead of testing the setter directly. And that's the problem.
It possible that JSON deserializer uses constructor generated by Lombok (not setters).
Have a look here:
Jackson deserialize default values missing
Related
I'm at a complete loss here. I have a class with overloaded setters for a property, and for the life of me cannot get Jackson to pick a correct setter. Stripping out the things not needed from the class, here's the base of what I've got:
class TestDTO {
#Setter(onMethod_={#JsonProperty})
#JsonProperty
protected CustomWrapper wrappedValues = new CustomWrapper();
#JsonIgnore
#XmlTransient
public RecordAudit setWrappedValues(List<WrappedObject> wrappedValues) {
this.customWrapper = new CustomWrapper(wrappedValues);
return this;
}
#JsonIgnore
#XmlTransient
public RecordAudit setWrappedValues(CustomWrapper customWrapper) {
this.customWrapper = customWrapper;
return this;
}
}
I have tried every combination I can think of of #JsonIgnore and #JsonProperty. I've tried just adding #JsonProperty to the #Setter annotation, I've tried only adding #JsonIgnore to the two custom setters, I've tried only #JsonProperty on the field itself, but no matter what I try, I get the following error:
Conflicting setter definitions for property "wrappedValues": ...#setWrappedValues(1 params) vs ...#setWrappedValues(1 params)
Does anyone have any ideas what's going on here? Using Jackson 2.12.4, so I think just #JsonProperty should be all that's needed, but as I mentioned above, that still results in the same error.
This is on JDK 11 if that makes a difference, I'm still new to 11, so am not sure how much that affects this.
You need to mark setter you want to use as com.fasterxml.jackson.annotation.JsonSetter.
class TestDTO {
protected CustomWrapper wrappedValues = new CustomWrapper();
public RecordAudit setWrappedValues(List<WrappedObject> wrappedValues) {
this.customWrapper = new CustomWrapper(wrappedValues);
return this;
}
#JsonSetter
public RecordAudit setWrappedValues(CustomWrapper customWrapper) {
this.customWrapper = customWrapper;
return this;
}
}
P.S. Your #Setter aren't generating anything since there are methods with name setWrappedValues
I have a class that is annotated with #Data. But I want to exclude the toString() method and provide a custom toString.
I just defined a custom toString() method as I usually do if I was not using lombok and it seemed to work. Following is my example.
#Data
class SomeDTO {
private String property1;
private String property2;
private String someReallyHugeString;
#Override
public String toString(){
return "someReallyHugeString size is: " + someReallyHugeString.length()
+ "property1 = " + property1
+ "property2 = " + property2;
}
}
But wanted to know if this is the right way to exclude toString() from #Data and if there are any side effects I am missing.
Yes.
It is right way. You can provide any method generated by lombok. It will check that method already exist and skip generation.
The same rule applies to the constructor (any explicit constructor will prevent #Data from generating one), as well as toString, equals, and all getters and setters
by Lombok Docs
Just don't use #Data (but provide all the other annotations) that is has:
#Getter
#Setter
#RequiredArgsConstructor
#EqualsAndHashCode
SomeDTO { ....
public String toString(){....}
}
This way if you remove toString by accident, it will not be generated.
I have done this before but forgot and couldn't find the answer easily online.
Let's say I have lombok on a POJO like
#Builder
#NoArgsConstructor
class Car {
private int gallons;
private int wheels;
private String name;
}
and I want to use the builder notation in some logic
public Car getCar(boolean isNew) {
<I dont know what type to put here> carBase = Car.builder().wheels(4);
if(!isNew) {
return carBase.gallons(10).build();
}
else {
return carBase.gallons(0).build();
}
}
What type should I use to fill in?
Okay, so I was actually running into this error Why is Lombok #Builder not compatible with this constructor? which was breaking my #Builder class.
Apparently lombok will generate a static nested class in the class annotated with #Builder called <classname>Builder, so to answer my original question there would be a valid class called Car.CarBuilder.
I like to make my objects immutable based on this article (Why objects must be immutable).
However, I am trying to parse an object using Jackson Object Mapper. I was initially getting JsonMappingException: No suitable constructor found for type [simple type, class ]: cannot instantiate from JSON object.
I could fix it as mentioned here, by providing a default constructor and making my fields non-final.
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
#AllArgsConstructor
// #NoArgsConstructor(access = AccessLevel.PRIVATE)
#Builder
#Data
public class School {
#NonNull
private final String schoolId;
#NonNull
private final String schoolName;
}
What is a good programming style that I should follow to overcome this problem? Is the only way around is to make my objects mutable?
Can I use a different mapper that does not use the default constructor?
You can use a Jackson factory (method annotated with #JsonCreator) that reads fields off a map and calls your non-default constructor:
class School {
//fields
public School(String id, String name) {
this.schoolId = id;
this.schoolName = name;
}
#JsonCreator
public static School create(Map<String, Object> object) {
return new School((String) object.get("schoolId"),
(String) object.get("schoolName"));
}
//getters
}
Jackson will call the create method with a Map version of the json. And this effectively solves the problem.
I believe your question looks for a Jackson solution, rather than a new pattern/style.
TL;DR: using lombok and avoiding a default constructor
make immutable data class using #Value
annotate all your fields with #JsonProperty("name-of-property")
add lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty to your lombok.config to copy those to generated constructors
create an all-args constructor annotated with #JsonCreator
example:
#Value
#AllArgsConstructor(onConstructor_ = #JsonCreator)
class School {
#JsonProperty("schoolId")
String schoolId;
#JsonProperty("schoolName")
String schoolName;
}
long answer
There is an imo better alternative to a static factory method annotated with #JsonCreator, and that is having a constructor for all Elements (as is required for immutable classes anyway). Annotate that with #JsonCreator and also annotate all parameters with #JsonProperty like this:
class School {
//fields
#JsonCreator
public School(
#JsonProperty("id") String id,
#JsonProperty("name") String name) {
this.schoolId = id;
this.schoolName = name;
}
//getters
}
Those are the options the #JsonCreator annotation gives you. It describes them like this in its documentation:
Single-argument constructor/factory method without JsonProperty annotation for the argument: if so, this is so-called "delegate creator", in which case Jackson first binds JSON into type of the argument, and then calls creator. This is often used in conjunction with JsonValue (used for serialization).
Constructor/factory method where every argument is annotated with either JsonProperty or JacksonInject, to indicate name of property to bind to
You might not even need to explicitly specify the parameter name under some circumstances. The documentation regarding that for #JsonCreator further states:
Also note that all JsonProperty annotations must specify actual name (NOT empty String for "default") unless you use one of extension modules that can detect parameter name; this because default JDK versions before 8 have not been able to store and/or retrieve parameter names from bytecode. But with JDK 8 (or using helper libraries such as Paranamer, or other JVM languages like Scala or Kotlin), specifying name is optional.
Alternatively this will also work nicely with lombok version 1.18.3 or up, where you can add lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty to your lombok.config and therefore have it copy the JsonProperty annotations to the constructor, given that you do annotate all fields with it (which one should do anyway imo). To put the #JsonCreator-annotation on the constructor, you can use the experimental onX feature. Using lombok's #Value for immutable data classes, your DTO then might just look like this (untested):
#Value
//#AllArgsConstructor(onConstructor = #__(#JsonCreator)) // JDK7 or below
#AllArgsConstructor(onConstructor_ = #JsonCreator) // starting from JDK8
class School {
#JsonProperty("schoolId")
String schoolId;
#JsonProperty("schoolName")
String schoolName;
}
I am using #XmlRootElement annotation to get XML data from the database.
Right now, if I put #XmlTransient to getters, the fields are ignored.
For example:
public class Student {
private Integer studentId;
private String studentName;
#XmlTransient // Do not get student id
public Integer getStudentId() {
return this.studentId;
}
public String getStudentName() {
return this.studentName;
}
...// Setter goes here
Then, student ids are not appear in the XML file.
However, can I do this in the opposite way? I want to specify fields that I want to have in the XML file - there are too many fields in the Student class.
My server(Spring Framework 3.2.3) also uses the Jackson library, so I wonder I could use it if that is possible.
You could annotate your class with:
#XmlAccessorType(XmlAccessType.NONE)
Now you have to explicitly map properties in order to be serialized. See the Javadoc: http://docs.oracle.com/javaee/7/api/javax/xml/bind/annotation/XmlAccessType.html