What is use of the annotation #JsonIgnore? - java

I'm joining tables with one to many cardinality, the classes I'm using refer to each other. And I'm using #JsonIgnore annotation with out understanding it deeply.

#JsonIgnore is used to ignore the logical property used in serialization and deserialization. #JsonIgnore can be used at setters, getters or fields.
If you add #JsonIgnore to a field or its getter method, the field is not going to be serialized.
Sample POJO:
class User {
#JsonIgnore
private int id;
private String name;
public int getId() {
return id;
}
#JsonIgnore
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Sample code for serialization:
ObjectMapper mapper = new ObjectMapper();
User user = new User();
user.setId(2);
user.setName("Bob");
System.out.println(mapper.writeValueAsString(user));
Console output:
{"name":"Bob"}

When serializing your object into Json, the fields that are tagged with #JsonIgnore will not be included in the serialized Json object. This attribute is read by the Json serialize using reflection.

The Jackson's #JsonIgnore annotation can be placed on fields, getters/settess and constructor parameters mark a property to be ignored during the serialization to JSON (or deserialization from JSON). Refer to the Jackson annotations reference documentation for more details.

Related

How to create different JSON output for a complex object

I would like to generate different Json output for the same complex Java object depending on the use case.
For example check the following code:
class Employee {
private Long id;
private String name;
private EmployeeDetail detail;
private Department department;
...
}
class Department {
private Long id;
private String name;
private String address;
...
}
class EmployeeDetail {
private Long id;
private int salary;
private Date birthDate;
...
}
If I convert Employee to Json all of the fields from Employee, EmployeeDetail and Department will be present. And it is good for one use case.
However in the second use case I would like to skip Department details except the id field but keep the complete EmployeeDetail.
I know that I can add something similar #JsonView(EmployeeView.Basic.class) to the id field in the Department class and use Json views. However for cleaner code I would like to solve it inside the Employee class something like this:
class Employee {
private Long id;
private String name;
#JsonAllFields
private EmployeeDetail detail;
#JsonIdOnly
private Department department;
...
}
At the moment I use the Jackson library but can switch if required.
i think you can use com.fasterxml.jackson.annotation.
#JsonIgnore is used to ignore the logical property used in serialization and deserialization. #JsonIgnore can be used at setter, getter or field. You can use it in the fields in Department class except on ID. There are so many ways to do it. Either you can allow the only getter in serialization etc.
Example:
#JsonIgnore(false)
private String id;
OR
#JsonIgnoreProperties({ "bookName", "bookCategory" })
public class Book {
#JsonProperty("bookId")
private String id;
#JsonProperty("bookName")
private String name;
#JsonProperty("bookCategory")
private String category;
}
To know more about it, please refer: https://www.concretepage.com/jackson-api/jackson-jsonignore-jsonignoreproperties-and-jsonignoretype
I hope this helps.
Just found a solution using #JsonFilter
Now the Employee class looks like this:
class Employee {
private Long id;
private String name;
private EmployeeDetail detail;
#JsonFilter("departmentFilter")
private Department department;
...
}
And the code to generate the limited json looks like this:
ObjectMapper mapper = new ObjectMapper();
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("departmentFilter", SimpleBeanPropertyFilter.filterOutAllExcept("id"));
mapper.setFilterProvider(filterProvider);
One small cons is that now I also need to define the filter to generate the full, detailed json like this:
filterProvider.addFilter("departmentFilter", SimpleBeanPropertyFilter.serializeAll());

Spring-data-rest jacksonHttpMessageConverter doesn't convert nested entity [duplicate]

My spring-data-rest integration test fails for a simple json request. Consider the below jpa models
Order.java
public class Order {
#Id #GeneratedValue//
private Long id;
#ManyToOne(fetch = FetchType.LAZY)//
private Person creator;
private String type;
public Order(Person creator) {
this.creator = creator;
}
// getters and setters
}
Person.java
ic class Person {
#Id #GeneratedValue private Long id;
#Description("A person's first name") //
private String firstName;
#Description("A person's last name") //
private String lastName;
#Description("A person's siblings") //
#ManyToMany //
private List<Person> siblings = new ArrayList<Person>();
#ManyToOne //
private Person father;
#Description("Timestamp this person object was created") //
private Date created;
#JsonIgnore //
private int age;
private int height, weight;
private Gender gender;
// ... getters and setters
}
In my test I created a person by using personRepository and inited order by passing person
Person creator = new Person();
creator.setFirstName("Joe");
creator.setLastName("Keith");
created.setCreated(new Date());
created.setAge("30");
creator = personRepository.save(creator);
Order order = new Order(creator);
String orderJson = new ObjectMapper().writeValueAsString(order);
mockMvc.perform(post("/orders").content(orderJson).andDoPrint());
Order is created but creator is not associated with the order. Also I want to pass request body as a json object. In this my json object should contain creator as follows
{
"type": "1",
"creator": {
"id": 1,
"firstName": "Joe",
"lastName": "Keith",
"age": 30
}
}
If I send request body with the following json, the call works fine
{
"type": "1",
"creator": "http://localhost/people/1"
}
But I don't want to send the second json. Any idea how to solve the issue. Because already my client is consuming the server response by sending first json. Now I migrated my server to use spring-data-rest. After that all my client code is not working.
How to solve this?
You are correctly associating order with the creator, however the Person is not associated with the orders. You are missing the List<Order> orders field in Person class. Add this, add annotations, add methods for adding order to person and then before sending JSON you should call something like this:
creator.addOrder(order);
order.setCreator(cretr);
Did you try using cascade = CascadeType.ALL in #ManyToOne annotation
public class Order {
#Id #GeneratedValue//
private Long id;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)//
private Person creator;
private String type;
public Order(Person creator) {
this.creator = creator;
}
// getters and setters
}
Both your Order and Person classes should implement Serializable to properly break them down into and rebuild them from JSON.
There are some ways to solve your problem, but I want give you a hint. You just can save only "id" of your person and get the person by "id" from your database, when you need this.
It solves your problem and it also saves the memory.
I believe you need to do two things to get this work.
Handle the deserialization properly. As you expect Jackson to populate the nested Person object via the constructor you need to annotate this with #JsonCreator. See here:
http://www.cowtowncoder.com/blog/archives/2011/07/entry_457.html
One of more powerful features of Jackson is its ability to use arbitrary >constructors for creating POJO instances, by indicating constructor to use with
#JsonCreator annotation
...........................................
Property-based creators are typically used to pass one or more
obligatory parameters into constructor (either directly or via factory
method). If a property is not found from JSON, null is passed instead
(or, in case of primitives, so-called default value; 0 for ints and so
on).
See also here on why Jackson may not be able to automatically work this out.
https://stackoverflow.com/a/22013603/1356423
Update your JPA mappings. If the associated Person is now populated correctly by the Jackson deserializer then by adding the necessary JPA cascade options to the relationship then both instances should be persisted.
I think then the following should work as expected:
public class Order {
#Id
#GeneratedValue(...)
private Long id;
#ManyToOne(fetch = FetchType.LAZY, cascade = cascadeType.ALL)
private Person creator;
private String type;
#JsonCreator
public Order(#JsonProperty("creator") Person creator) {
this.creator = creator;
}
}

How can you ignore a field during serialization but not during deserialization?

I have a POJO class
class Human{
String name;
Integer age;
//get and set
}
When I deserialize json to a Human object I want to read both fields (age and name values). But when I serialize a Human object to json I want to ignore age.
Is this possible ?
The javadoc of #JsonIgnore states
In addition, starting with Jackson 1.9, if this is the only annotation
associated with a property, it will also cause cause the whole
property to be ignored: that is, if setter has this annotation and
getter has no annotations, getter is also effectively ignored. It is
still possible for different accessors to use different annotations;
so if only "getter" is to be ignored, other accessors (setter or
field) would need explicit annotation to prevent ignoral (usually
JsonProperty).
So just annotate the getter and setter appropriately
// for serialization
#JsonIgnore
public String getName() {
return name;
}
// for deserialization
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}

Only using #JsonIgnore during serialization, but not deserialization

I have a user object that is sent to and from the server. When I send out the user object, I don't want to send the hashed password to the client. So, I added #JsonIgnore on the password property, but this also blocks it from being deserialized into the password that makes it hard to sign up users when they don't have a password.
How can I only get #JsonIgnore to apply to serialization and not deserialization? I'm using Spring JSONView, so I don't have a ton of control over the ObjectMapper.
Things I've tried:
Add #JsonIgnore to the property
Add #JsonIgnore on the getter method only
Exactly how to do this depends on the version of Jackson that you're using. This changed around version 1.9, before that, you could do this by adding #JsonIgnore to the getter.
Which you've tried:
Add #JsonIgnore on the getter method only
Do this, and also add a specific #JsonProperty annotation for your JSON "password" field name to the setter method for the password on your object.
More recent versions of Jackson have added READ_ONLY and WRITE_ONLY annotation arguments for JsonProperty. So you could also do something like:
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
Docs can be found here.
In order to accomplish this, all that we need is two annotations:
#JsonIgnore
#JsonProperty
Use #JsonIgnore on the class member and its getter, and #JsonProperty on its setter. A sample illustration would help to do this:
class User {
// More fields here
#JsonIgnore
private String password;
#JsonIgnore
public String getPassword() {
return password;
}
#JsonProperty
public void setPassword(final String password) {
this.password = password;
}
}
Since version 2.6: a more intuitive way is to use the com.fasterxml.jackson.annotation.JsonProperty annotation on the field:
#JsonProperty(access = Access.WRITE_ONLY)
private String myField;
Even if a getter exists, the field value is excluded from serialization.
JavaDoc says:
/**
* Access setting that means that the property may only be written (set)
* for deserialization,
* but will not be read (get) on serialization, that is, the value of the property
* is not included in serialization.
*/
WRITE_ONLY
In case you need it the other way around, just use Access.READ_ONLY.
In my case, I have Jackson automatically (de)serializing objects that I return from a Spring MVC controller (I am using #RestController with Spring 4.1.6). I had to use com.fasterxml.jackson.annotation.JsonIgnore instead of org.codehaus.jackson.annotate.JsonIgnore, as otherwise, it simply did nothing.
Another easy way to handle this is to use the argument allowSetters=truein the annotation. This will allow the password to be deserialized into your dto but it will not serialize it into a response body that uses contains object.
example:
#JsonIgnoreProperties(allowSetters = true, value = {"bar"})
class Pojo{
String foo;
String bar;
}
Both foo and bar are populated in the object, but only foo is written into a response body.
"user": {
"firstName": "Musa",
"lastName": "Aliyev",
"email": "klaudi2012#gmail.com",
"passwordIn": "98989898", (or encoded version in front if we not using https)
"country": "Azeribaijan",
"phone": "+994707702747"
}
#CrossOrigin(methods=RequestMethod.POST)
#RequestMapping("/public/register")
public #ResponseBody MsgKit registerNewUsert(#RequestBody User u){
root.registerUser(u);
return new MsgKit("registered");
}
#Service
#Transactional
public class RootBsn {
#Autowired UserRepository userRepo;
public void registerUser(User u) throws Exception{
u.setPassword(u.getPasswordIn());
//Generate some salt and setPassword (encoded - salt+password)
User u=userRepo.save(u);
System.out.println("Registration information saved");
}
}
#Entity
#JsonIgnoreProperties({"recordDate","modificationDate","status","createdBy","modifiedBy","salt","password"})
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String country;
#Column(name="CREATED_BY")
private String createdBy;
private String email;
#Column(name="FIRST_NAME")
private String firstName;
#Column(name="LAST_LOGIN_DATE")
private Timestamp lastLoginDate;
#Column(name="LAST_NAME")
private String lastName;
#Column(name="MODIFICATION_DATE")
private Timestamp modificationDate;
#Column(name="MODIFIED_BY")
private String modifiedBy;
private String password;
#Transient
private String passwordIn;
private String phone;
#Column(name="RECORD_DATE")
private Timestamp recordDate;
private String salt;
private String status;
#Column(name="USER_STATUS")
private String userStatus;
public User() {
}
// getters and setters
}
You can use #JsonIgnoreProperties at class level and put variables you want to igonre in json in "value" parameter.Worked for me fine.
#JsonIgnoreProperties(value = { "myVariable1","myVariable2" })
public class MyClass {
private int myVariable1;,
private int myVariable2;
}
You can also do like:
#JsonIgnore
#JsonProperty(access = Access.WRITE_ONLY)
private String password;
It's worked for me
I was looking for something similar. I still wanted my property serialized but wanted to alter the value using a different getter. In the below example, I'm deserializing the real password but serializing to a masked password. Here's how to do it:
public class User() {
private static final String PASSWORD_MASK = "*********";
#JsonIgnore
private String password;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
public String setPassword(String password) {
if (!password.equals(PASSWORD_MASK) {
this.password = password;
}
}
public String getPassword() {
return password;
}
#JsonProperty("password")
public String getPasswordMasked() {
return PASSWORD_MASK;
}
}
The ideal solution would be to use DTO (data transfer object)

How to change a field name in JSON using Jackson

I'm using jackson to convert an object of mine to json.
The object has 2 fields:
#Entity
public class City {
#id
Long id;
String name;
public String getName() { return name; }
public void setName(String name){ this.name = name; }
public Long getId() { return id; }
public void setName(Long id){ this.id = id; }
}
Since I want to use this with the jQuery auto complete feature I want 'id' to appear as 'value' in the json and 'name' to appear as 'label'. The documentation of jackson is not clear on this and I've tried every annotation that even remotely seems like it does what I need but I can't get name to appear as label and id to appear as value in the json.
Does anyone know how to do this or if this is possible?
Have you tried using #JsonProperty?
#Entity
public class City {
#id
Long id;
String name;
#JsonProperty("label")
public String getName() { return name; }
public void setName(String name){ this.name = name; }
#JsonProperty("value")
public Long getId() { return id; }
public void setId(Long id){ this.id = id; }
}
Be aware that there is org.codehaus.jackson.annotate.JsonProperty in Jackson 1.x and com.fasterxml.jackson.annotation.JsonProperty in Jackson 2.x. Check which ObjectMapper you are using (from which version), and make sure you use the proper annotation.
Jackson
If you are using Jackson, then you can use the #JsonProperty annotation to customize the name of a given JSON property.
Therefore, you just have to annotate the entity fields with the #JsonProperty annotation and provide a custom JSON property name, like this:
#Entity
public class City {
#Id
#JsonProperty("value")
private Long id;
#JsonProperty("label")
private String name;
//Getters and setters omitted for brevity
}
JavaEE or JakartaEE JSON-B
JSON-B is the standard binding layer for converting Java objects to and from JSON. If you are using JSON-B, then you can override the JSON property name via the #JsonbProperty annotation:
#Entity
public class City {
#Id
#JsonbProperty("value")
private Long id;
#JsonbProperty("label")
private String name;
//Getters and setters omitted for brevity
}
There is one more option to rename field:
Jackson MixIns.
Useful if you deal with third party classes, which you are not able to annotate, or you just do not want to pollute the class with Jackson specific annotations.
The Jackson documentation for Mixins is outdated, so this example can provide more clarity. In essence: you create mixin class which does the serialization in the way you want. Then register it to the ObjectMapper:
objectMapper.addMixIn(ThirdParty.class, MyMixIn.class);

Categories