1. Overview
In this article, we will learn to serialize or deserialize private fields using Jackson without setters or getters. To serialize or deserialize, you can use ObjectMapper class of the Jackson library.
Let’s understand how Jackson performs serialization or deserialization. Jackson can handle serialization or deserialization without getter or setter methods for a public field.
Let’s discuss the serialization and deserialization behaviors when the Java field is private.
2. Jackson deserialize private fields
Jackson uses the setter and getter methods to auto-detect the private field and updates the field during deserialization.
Assume you have defined all the fields or properties in your Java class so that both input JSON and output Java class have identical fields. But missing setter and getter methods of private fields would cause the UnrecognizedPropertyException
error.
The following Student
class contains a private id
field without setter or getter methods.
public class Student { public Student(long id) { this.id = id; } private long id; @Override public String toString() { return "Student [id=" + id + "]"; } }
If you execute the following code, it leads to UnrecognizedPropertyException
error:
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "id" (class com.tedblob.jackson.fieldname.models.Student), not marked as ignorable (0 known properties: ]) at [Source: (String)"{"id": 1001}"; line: 1, column: 12] (through reference chain: com.tedblob.jackson.fieldname.models.Student["id"])
@Test void privateFields_withoutSetterOrGetter() throws JsonMappingException, JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); String json = "{\"id\": 1001}"; Student student = mapper.readValue(json, Student.class); System.out.println(student); }
3. Jackson serialize private fields
Similarly, trying to serialize the Student
object private id
field would cause the following error:
@Test void privateFields_serialize_withoutSetterOrGetter() throws JsonMappingException, JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); Student student = new Student(); String studentStr = mapper.writeValueAsString(student); System.out.println(studentStr); }
Since the Student
class doesn’t have any fields or properties to serialize, Jackson throws the FAIL_ON_EMPTY_BEANS error.
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com.tedblob.jackson.examples.models.Student and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
Now, let’s discuss the solutions to auto-detect the private field.
4. Change Java field visibility at class level
By default, Jackson detects a private field only using the setter or getter method. However, you can customize this behavior by using the @JsonAutoDetect annotation. You can set the field visibility as Visibility.ANY:
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
Setting this @JsonAutoDetect
with Visibility.ANY
annotation would make the Jackson auto-detect fields with any access modifier from private to public.
The below Employee
class has the private fields id
and name
without setter or getter methods. Visibility ANY allows Jackson to auto-detect these private fields.
@JsonAutoDetect(fieldVisibility = Visibility.ANY) public class Employee { private long id; private String name; @Override public String toString() { return "Employee [id=" + id + ", name=" + name + "]"; } }
@Test void privateFields_withVisibilityAny() throws JsonMappingException, JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); String json = "{\"id\": 1001, \"name\": \"John\"}"; Employee employee = mapper.readValue(json, Employee.class); System.out.println(employee); }
5. Change Java field visibility globally
Alternatively, you can make the Jackson auto-detect fields with any access modifiers by configuring the ObjectMapper
:
ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
Assume the Student
class contains a private field id
.
public class Student { public Student(long id) { this.id = id; } private long id; @Override public String toString() { return "Student [id=" + id + "]"; } }
The below code contains the ObjectMapper
instance whose field visibility is set to ANY. So Jackson detects the id
field and deserializes the JSON to the Student
object.
@Test void privateFields_withoutSetterOrGetter_Visibility() throws JsonMappingException, JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); String json = "{\"id\": 1001}"; Student student; student = mapper.readValue(json, Student.class); System.out.println(student); }
6. Ignore FAIL_ON_EMPTY_BEANS exception – Jackson private fields
Jackson throws FAIL_ON_EMPTY_BEANS exception when it cannot serialize the provided Java object. This happens when the Java object doesn’t have any accessors or annotations to show that it can be serialized.
However, you can disable this behavior by configuring the mapper to disable the FAIL_ON_EMPTY_BEANS error. Running the below code simply returns empty JSON {} throwing no exception.
@Test void privateFields_serialize_FailOnBeans() throws JsonMappingException, JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); Student student = new Student(); String studentStr = mapper.writeValueAsString(student); System.out.println(studentStr); }
7. Accessor methods for Java private field
At last, you can add accessor methods to your Java field if it is acceptable to do. Jackson can handle deserialization when the setter method is available to your Java private field. However, it requires the getter method to perform serialization.
- Setter method enables Deserialization
- Getter method enables both deserialization and serialization
For example, the below Department
class contains deptId
and deptName
fields. The private deptName
property contains only the setter method.
public class Department { private String deptName; private long deptId; public void setDeptName(String deptName) { this.deptName = deptName; } public long getDeptId() { return deptId; } public void setDeptId(long deptId) { this.deptId = deptId; } @Override public String toString() { return "Department [deptName=" + deptName + ", deptId=" + deptId + "]"; } }
Since the private deptName
contains the setter method, the deserialization code works properly.
@Test void privateFields_withSetterOnly() throws JsonMappingException, JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); String json = "{\"deptId\": 1001, \"deptName\": \"IT\"}"; Department department = mapper.readValue(json, Department.class); assertThat(department); }
3. Conclusion
To sum up, we have learned the various solutions to detect private field during serialization or deserialization.
Pingback: Jackson UnrecognizedPropertyException: field Ignorable - TedBlob