Jackson deserialize snake case to camel case

Jackson deserialize snake case to camel case

1. Introduction

In this article, we will learn to deserialize the snake case to the camel case using Jackson library. Let’s first understand the differences between camel case and snake case.

A camel case (CamelCase or camelCase) is a convention of writing a compound word with no spaces, expressing the separation of words with a single capitalized letter, and the first word starting with either lower case or upper case. If the first word starts with lower case (camelCase), then we refer to it as lower camel case or dromedary case.

In Java, we normally use the lower camel case as a naming convention for methods and fields.

A Snake case (snake_case) is a convention of writing a compound word separated by underscore (_) character and the first letter of each word stating with lowercase.

2. Jackson default behavior - deserialize snake to camel case

Assume your JSON uses the snake case style for its fields, whereas your Java class uses the camel case. Since both JSON and Java class uses a different naming style, deserialization will not work. So you need to configure your Jackson library to handle this.

For example, the below Student class contains fields id and studentName that uses lower camel case.

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class Student {
	public Student(long id, String studentName) {
		this.id = id;
		this.studentName = studentName;
	}
	public Student() {	}
	private long id;
	private String studentName;
	@Override
	public String toString() {
		return "Student [id=" + id + ", studentName=" + studentName + "]";
	}
}

The below json contains fields id and student_name using snake case as naming style.

@Test
void snakeToCamel_Default() throws JsonMappingException, JsonProcessingException {
	ObjectMapper mapper = new ObjectMapper();
	String json = "{\"id\": 1001, \"student_name\":\"Praj\"}";
	Student student = mapper.readValue(json, Student.class);
	System.out.println(student);
}

When you convert or deserialize the json to java class Student using the ObjectMapper jackson class, then the following error shows up.

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "student_name" (class com.tedblob.jackson.examples.models.snaketocamel.Student), not marked as ignorable (2 known properties: "id", "studentName"])
 at [Source: (String)"{"id": 1001, "student_name":"Praj"}"; line: 1, column: 30] (through reference chain: com.tedblob.jackson.examples.models.snaketocamel.Student["student_name"])

All the below solutions would work with both serialization (camel case Java object to snake case) and deserialization (JSON snake case to camel case Java object).

3. Jackson deserialize snake case to camel case

Let’s see the solutions to change this default behavior and make Jackson overcome underscores in favor of the camel case.

3.1. Using properties file

If you are using Spring Boot in your project, then you can update the below property in your application.properties file. The property should reflect the style of JSON.

spring.jackson.property-naming-strategy=SNAKE_CASE
spring.jackson.property-naming-strategy=CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
spring:
  jackson:
    property-naming-strategy: SNAKE_CASE

The above property accepts the constants of class PropertyNamingStrategies as input: LOWER_CAMEL_CASE, UPPER_CAMEL_CASE, SNAKE_CASE, LOWER_CASE, LOWER_DOT_CASE, KEBAB_CASE

In order for this property to work, your ObjectMapper instance should be created by Spring.

You can use Jackson2ObjectMapperBuilder to build your ObjectMapper instance as follows:

@SpringBootTest
public class JacksonSnakeToCamelTest {
	@Autowired
	Jackson2ObjectMapperBuilder builder;
	@Test
	void snakeToCamel_Builder() throws JsonMappingException, JsonProcessingException {
		ObjectMapper mapper = builder.build();
		String json = "{\"id\": 1001, \"student_name\":\"Praj\"}";
		Student student = mapper.readValue(json, Student.class);
		System.out.println(student);
	}
}

Alternatively, you can create an ObjectMapper Bean and use it. For example, create a @Bean method that returns an ObjectMapper in a @Configuration class.

@TestConfiguration
public class TestConfig {
    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
}
@Autowired
ObjectMapper mapper;
@Test
void snakeToCamel_Bean() throws JsonMappingException, JsonProcessingException {
	String json = "{\"id\": 1001, \"student_name\":\"Praj\"}";
	Student student = mapper.readValue(json, Student.class);
	System.out.println(student);
}

The ObjectMapper correctly converts the JSON snake case fields to Java camel case fields. So running the above @Test methods returns:

Student [id=1001, studentName=Praj]

3.2. Configure the ObjectMapper

Alternatively, you can configure the ObjectMapper directly. We can use the setPropertyNamingStrategy method to configure ObjectMapper.

This method accepts constants of either PropertyNamingStrategies or PropertyNamingStrategy. As of Jackson 2.12, PropertyNamingStrategy is deprecated and PropertyNamingStrategies class is added.

mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); 
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
@Test
void snakeToCamel_NamingStrategy() throws JsonMappingException, JsonProcessingException {
	ObjectMapper mapper = new ObjectMapper();
        mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
	String json = "{\"id\": 1001, \"student_name\":\"Praj\"}";
	Student student = mapper.readValue(json, Student.class);
	System.out.println(student);
}

The above test runs fine and prints the result in the console:

Student [id=1001, studentName=Praj]

3.3. @JsonNaming annotation

You can use @JsonNaming annotation to mention the property naming strategy. This annotation accepts PropertyNamingStrategy implementation classes. As of Jackson 2.12, PropertyNamingStrategy is deprecated and PropertyNamingStrategies class is added.

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)

3.3.1. Jackson deserialize snake to camel case

For example, the below Dapartment class contains deptId and deptName with lower case style. Also, we annotated the class with @JsonNaming to inform Jackson about the JSON’s naming style.

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class Department {
	private String deptName;
	private long deptId;
	@Override
	public String toString() {
		return "Department [deptName=" + deptName + ", deptId=" + deptId + "]";
	}
}
@Test
void snakeToCamel_JsonNaming() throws JsonMappingException, JsonProcessingException {
	ObjectMapper mapper = new ObjectMapper();
	String json = "{\"dept_id\": 1001, \"dept_name\":\"Praj\"}";
	Department department = mapper.readValue(json, Department.class);
	System.out.println(department);
 }

When you deserialize using the ObjectMapper class, the result Department object would be:

Department [deptName=Praj, deptId=1001]

3.3.2. Serialize from camel to snake case

Similarly, you can serialize from Java lower camel case to snake case JSON fields using the @JsonNaming annotation.

@Test
void CamelToSnake_JsonNaming() throws JsonMappingException, JsonProcessingException {
	ObjectMapper mapper = new ObjectMapper();
	Department department = new Department(12123, "Mike");
	System.out.println(mapper.writeValueAsString(department));
}

If you run the above test case, you would see the following JSON with snake case fields in the console:

{"dept_name":"Mike","dept_id":12123}

3.4. @JsonProperty to overcome underscore in favor of camel case

If you have only very few fields, then you can use @JsonProperty on each Java field to inform Jackson on the corresponding JSON field to be used. To know more about changing the name of a property, refer to this article.

The below code contains the Employee object with empId and employeeName fields annotated with @JsonProperty.

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class Employee {
	@JsonProperty("emp_id")
	private long empId;
	@JsonProperty("emp_name")
	private String employeeName;
	@Override
	public String toString() {
		return "Employee [id=" + empId + ", name=" + employeeName + "]";
	}
}
@Test
void snakeToCamel_JsonProperty() throws JsonMappingException, JsonProcessingException {
	ObjectMapper mapper = new ObjectMapper();
	String json = "{\"emp_id\": 1001, \"emp_name\":\"Praj\"}";
	Employee emp = mapper.readValue(json, Employee.class);
	System.out.println(emp);
}

Running the above code results in the below Employee object:

Employee [id=1001, name=Praj]

4. Conclusion

To sum up, we learned the solutions to overcome underscores in favor of camel case. Above solutions work in case of both serialization and deserialization.

Leave a Comment