Published on

Setting Up Jackson To Work With JavaScript Naming Conventions

Authors

Recently I had to expose a service that consumed data sent in mostly JavaScript style naming conventions.

What I mean by this is although REST services normally accept JSON which is JavaScript, the names of the fields in that request could follow any convention.

For example, the property name first name could follow the more Java-style camel case naming convention firstName or the more JavaScript style snake case convention first_name.

Out of the box, Jackson supports the more Java-style camel case convention.

After a bit of tweaking I eventually came up with the following Jackson config:

JsonMapper.builder()

    .addModules(KotlinModule(), JavaTimeModule())

    .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
    .serializationInclusion(JsonInclude.Include.NON_NULL)

    .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)

    .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)

    .build()

This does the following

  • addModules(KotlinModule(), JavaTimeModule())
    • KotlinModule makes Jackson aware of Kotlin data classes
    • JavaTimeModule makes Jackson aware of how to work with LocalDate, LocalDateTime and their similar classes
  • .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
    • This allows ISO 8601 date-time strings to be used for date objects
  • .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    • This allows other properties to be passed through to your object without Jackson failing. This is really useful if you are trying to work with large JSON objects you do not have control over but you only want some fields (this way you only map the fields you want and ignore the rest)
  • .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
    • This allows JSON keys to include quotes "first_name": "John" or to exclude them: first_name: "John"
  • .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
    • This makes Jackson expect lowercase enum values i.e. "north" instead of "NORTH"
  • .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
    • This property is what makes Jackson use the snake case naming convention over the normal camel case convention

To use this in spring is as easy as adding a configuration as follows:

package com.your.awesome.company.config

import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.KotlinModule
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class JacksonConfiguration {

    @Bean
    fun objectMapper(): ObjectMapper = JsonMapper.builder()

        .addModules(KotlinModule(), JavaTimeModule())

        .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
        .serializationInclusion(JsonInclude.Include.NON_NULL)

        .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)

        .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)

        .build()
}