I'm running into an issue with my pojo created using lombok with jsonproperty annotation. It doesn't respect the json annotation. And, when i create an object using the lombok builder it uses the field names on the object instead of json property.
Could someone help see what am I missing here. I just started using lombok so im hoping something straightforward. I'm running the code on Intellij
#Data
#Builder
public class pojo {
#JsonProperty("grant_type")
private final String grantType = "xyz";
#JsonProperty("client_id")
private String clientId;
}
It's default behavior of #Builder.
If we want the builder with setClientId, We can add setterPrefix = "set" into #Builder.
#Data
#Builder(setterPrefix = "set")
public class pojo {
#JsonProperty("grant_type")
private final String grantType = "xyz";
#JsonProperty("client_id")
private String clientId;
}
#Data would generate a pair of setter/getter. But setter is a member method of pojo, not pojoBuilder's.
More details of Builder is here.
Related
I'm trying to de-serialize class from JSON string using library com.alibaba.fastjson. This is the class:
#Getter
#ToString
#Setter
#JSONType(naming = PropertyNamingStrategy.CamelCase)
public class SiteInfo {
private String id;
...
#JSONField(name = "keywords")
private String keyWords;
...
}
After I run the code, the value of keyWords=null; In other hand, if I changing it to "private String keywords;" everything works fine. So I can't use the camelcase (according to java conventions).
In need some thing similar like #JsonProperty("keywords") from Jackson library, but for the alibaba fast json.
This is definitely not a duplicate of Only using #JsonIgnore during serialization, but not deserialization. The problem is the same but in the context of Immutables.
When a model(DTO/DAO) is decorated as an Immutable, I am not able to selectively #JsonIgnore one of the properties during serialization. Suppose that we have a UserDto which is defined as an Immutable as follow
#Value.Immutable
#Value.Style(defaults = #Value.Immutable(copy = false), init = "set*")
#JsonSerialize(as = ImmutableUserDto.class)
#JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto {
#JsonProperty("id")
#Value.Default
public int getId() {
return 0;
}
#JsonProperty("username")
public abstract String getUsername();
#JsonProperty("email")
public abstract String getEmail();
#JsonProperty("password")
public abstract String getPassword();
}
I believe it is fair to expect that during serialization we would want to ignore the password from the response of the service.
Without using Immutables if we were working with a simple class, then there are many ways to accomplish this. For example - annotate only the getter with #JsonIgnore. Or if possible define a different accessor method (something that doesn't have the get prefix) and only define the regular setter method... and so on.
If I try the same on the Immutables accessor method for the password as shown below:
#Value.Immutable
#Value.Style(defaults = #Value.Immutable(copy = false), init = "set*")
#JsonSersonIgnoreialize(as = ImmutableUserDto.class)
#JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto {
....
#JsonProperty("password")
#JsonIgnore
public abstract String getPassword();
}
then, the generated ImmutableUserDto adds the #JsonIgnore on both the getter and setter as shown below.
#Generated(from = "UserDto", generator = "Immutables")
#SuppressWarnings({"all"})
#ParametersAreNonnullByDefault
#javax.annotation.Generated("org.immutables.processor.ProxyProcessor")
#Immutable
#CheckReturnValue
public final class ImmutableUserDto extends UserDto {
...
...
private final String password;
...
...
/**
* #return The value of the {#code password} attribute
*/
#JsonProperty("password")
#JsonIgnore
#Override
public String getPassword() {
return password;
}
...
...
...
#Generated(from = "UserDto", generator = "Immutables")
#NotThreadSafe
public static final class Builder {
...
...
private String password;
#JsonProperty("password")
#JsonIgnore
public final Builder setPassword(String password) {
this.password = password;
return this;
}
}
}
Serialization will work as expected. The password attribute will be excluded from the JSON. But when I try to de-serialize, I get the following error:
java.lang.IllegalStateException: Cannot build UserDto, some of the required attributes are not set [password]
Which is obvious as Immutables added the #JsonIgnore to the setter as well.
The documentation isn't of much help. In their Things to be aware of section, it just mentions the following regarding #JsonIgnore
If using #JsonIgnore, you should explicitly make an attribute
non-mandatory. In Immutables, an attribute can be declared as
non-mandatory via #Nullable, Optional or #Value.Default which are all
different in their effect and we do not derive anything automatically.
Using #Nullable or Optional or #Value.Default is not of any use in case of fields like password.
I have gone through the issue list on their GitHub page and there is a similar issue but the user was asking for a slightly different use case and using #Nullable could solve the problem which doesn't work in my case.
I have also tried to use one of the answers here. Still resulted in the same error.
It looked like this is not supported by Immutables library. I have created a new issue myself. Once I get some feedback from users on SOF, I will probably create a sscce.
I had to use the suggestion given by #benarena in this comment. However I had to explicitly specify the value attribute of the property along with the Access attribute.
#JsonProperty(value = "password", access = JsonProperty.Access.WRITE_ONLY) solved the problem.
The Immutable class would look like:
#Value.Immutable
#Value.Style(defaults = #Value.Immutable(copy = false), init = "set*")
#JsonSersonIgnoreialize(as = ImmutableUserDto.class)
#JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto {
....
#JsonProperty(value = "password", access = JsonProperty.Access.WRITE_ONLY)
public abstract String getPassword();
}
Using json to save and load data requires a constructor for json to load the object, and I'm having trouble getting lombok annotations to work with this. What should I do?
This is what my class looked like before and after attempting to use an annotation to construct my item:
#Data
public class Item { //before
private int id;
private int amount;
public Item(#JsonProperty("id") int id, #JsonProperty("amount") int amount) {
this.id = id;
this.amount = amount;
}
}
#Data
#AllArgsConstructor
#NoArgsConstructor //I don't want this here as it could cause complications in other places. But json requires I have this...
public class Item { //after
private int id;
private int amount;
}
I don't want to use the NoArgsConstructor annotation by lombok as I don't want a no args constructor for this class. I realise that I could do this:
private Item() {
}
But was hoping there is a better way...
Since lombok 1.18.4, you can configure what annotations are copied to the constructor parameters. Insert this into your lombok.config:
lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty
Then just add #JsonProperty to your fields:
#Data
#AllArgsConstructor
public class Item {
#JsonProperty("id")
private int id;
#JsonProperty("amount")
private int amount;
}
Although the annotation parameters may seem unnecessary, they are in fact required, because at runtime the names of the constructor parameters are not available.
try adding this to your lombok config file:
lombok.anyConstructor.addConstructorProperties=true
config.stopBubbling = true
So what you're saying is that Jackson requires no-args constructor for deserialization, and you don't want to add no-args constructors to your classes because that doesn't play well with your model.
Lombok is completely irrelevant here - it makes zero difference whether no-args constructor would be written manually or generated by Lombok, it'll still be just a no-args constructor.
Your real question is - can I make Jackson work without no-argument constructors on target classes. There are multiple answers to that already, you have almost done it. Here's what has to be done:
Add #JsonCreator annotation to your constructor
Add #JsonProperty("propName") to constructor parameters
You did the #2 but not #1. Add that and this should fix your problem.
I have a JPA transient property in an entity which has a calculated value based on multiple fields in the POJO. All these calculations are done in the GETTER of that property.
But, Jackson doesnt seem to be using the GETTER when creating the JSON for that POJO.
How do I configure Jackson to use getter for the property?
My POJO looks something like below
#Entity
public class ProductSummaryEntity implements Serializable {
#Basic
private String field1;
// GETTER and SETTER for Field1
#Basic
private String field2;
// GETTER and SETTER for Field2
#Transient
private String field3;
public String getField3(){
setField3(field1 + field2);
return this.field3;
}
public void setField3(String temp){
this.field3=temp;
}
}
This link to a blog by #sghill has been posted on SO before and shows you how to customize the serialization process: https://www.sghill.net/how-do-i-write-a-jackson-json-serializer-deserializer.html
Essentially, annotate your POJO with #JsonSerialize(using = CustomSerializer.class) and then implement a class CustomSerializer that's extending from JsonSerializer. In your implementation you can build the JSON however you like and calculate values on the fly or call your getters.
No, I don't think you can serialize transient field unless there is something latest is there.
If I specify #AllArgsConstructor using Lombok, it will generate a constructor for setting all the declared (not final, not static) fields.
Is it possible to omit some field and this leave generated constructor for all other fields?
No that is not possible. There is a feature request to create a #SomeArgsConstructor where you can specify a list of involved fields.
Full disclosure: I am one of the core Project Lombok developers.
Alternatively, you could use #RequiredArgsConstructor. This adds a constructor for all fields that are either #NonNull or final.
See the documentation
Just in case it helps, initialized final fields are excluded.
#AllArgsConstructor
class SomeClass {
final String s;
final int i;
final List<String> list = new ArrayList<>(); // excluded in constructor
}
var x = new SomeClass("hello", 1);
It makes sense especially for collections, or other mutable classes.
This solution can be used together with the other solution here, about using #RequiredArgsConstructor:
#RequiredArgsConstructor
class SomeClass2 {
final String s;
int i; // excluded because it's not final
final List<String> list = new ArrayList<>(); // excluded because it's initialized
}
var x = new SomeClass2("hello");
A good way to go around it in some cases would be to use the #Builder
This can be done using two annotations from lombok #RequiredArgsConstructor and #NonNull.
Please find the example as follows
package com.ss.model;
import lombok.*;
#Getter
#Setter
#RequiredArgsConstructor
#ToString
public class Employee {
private int id;
#NonNull
private String firstName;
#NonNull
private String lastName;
#NonNull
private int age;
#NonNull
private String address;
}
And then you can create an object as below
Employee employee = new Employee("FirstName", "LastName", 27, "Address");
Lombok is meant to take care of the boilerplate code for your POJOs. Customized constructors/setters/getters/toString/copy etc are not on the boilerplate side of code. For these cases, every Java IDE provide easy to use code generators to help you do things in no time.
In your case a
public MyClass(String firstName, String lastName) {....}
is much more readable and makes more sense than a hypothetic:
#AllArgsConstructor(exclude = "id", exclude = "phone")
Have fun!