Skip to content

Jackson private fields – serialize/deserialize

Jackson private fields - serialize/deserialize

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.

  1. Setter method enables Deserialization
  2. 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.

1 thought on “Jackson private fields – serialize/deserialize”

  1. Pingback: Jackson UnrecognizedPropertyException: field Ignorable - TedBlob

Leave a Reply

Your email address will not be published. Required fields are marked *