Backend Development 7 min read

Customizing JSON Serialization and Deserialization in Spring Boot

In Spring Boot you can customize JSON request and response handling by annotating fields with @JsonSerialize/@JsonDeserialize, registering a global ObjectMapper module, adding a PropertyEditor via @ControllerAdvice, creating a custom HttpMessageConverter, or using AOP interception, selecting the approach that matches your project’s complexity.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Customizing JSON Serialization and Deserialization in Spring Boot

In Spring Boot, customizing request and response JSON can be achieved by several approaches.

1. Use @JsonSerialize and @JsonDeserialize on fields to specify custom serializer/deserializer.

public class CustomLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
    @Override
    public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }
}

public class CustomLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
    @Override
    public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        return LocalDateTime.parse(p.getValueAsString(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
}

public class MyEntity {
    @JsonSerialize(using = CustomLocalDateTimeSerializer.class)
    @JsonDeserialize(using = CustomLocalDateTimeDeserializer.class)
    private LocalDateTime dateTime;
    // getters and setters
}

2. Configure a global ObjectMapper by registering a custom module.

public class CustomJacksonModule extends SimpleModule {
    public CustomJacksonModule() {
        addSerializer(LocalDateTime.class, new CustomLocalDateTimeSerializer());
        addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer());
    }
}

@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
    return builder -> builder.modules(new CustomJacksonModule());
}

3. Use @ControllerAdvice with @InitBinder to register a custom PropertyEditor for form data.

public class CustomLocalDateTimeEditor extends PropertyEditorSupport {
    @Override
    public void setAsText(String text) {
        setValue(LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }
    @Override
    public String getAsText() {
        LocalDateTime value = (LocalDateTime) getValue();
        return value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
}

@ControllerAdvice
public class CustomControllerAdvice {
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(LocalDateTime.class, new CustomLocalDateTimeEditor());
    }
}

4. Implement a custom HttpMessageConverter to handle specific media types.

public class CustomLocalDateTimeConverter extends MappingJackson2HttpMessageConverter {
    public CustomLocalDateTimeConverter() {
        ObjectMapper customObjectMapper = new ObjectMapper();
        customObjectMapper.registerModule(new CustomJacksonModule());
        setObjectMapper(customObjectMapper);
    }
}

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(0, new CustomLocalDateTimeConverter());
    }
}

5. Apply Spring AOP to intercept controller methods and modify input/output, though using AOP for JSON transformation is generally discouraged.

@Aspect
@Component
public class CustomSerializationAspect {
    @Before("execution(* com.example.controller..*.*(..)) && args(..,@RequestParam(..),@RequestBody(..))")
    public void beforeControllerMethod(JoinPoint joinPoint) { /* optional */ }

    @AfterReturning(pointcut = "execution(* com.example.controller..*.*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)", returning = "result")
    public void afterControllerMethod(JoinPoint joinPoint, Object result) { /* optional */ }
}

Choose the method that fits the project’s complexity: simple field‑level changes with annotations, global mapper configuration, or a full‑blown converter/AOP for advanced scenarios.

JavaAOPSerializationJSONSpring BootJacksondeserialization
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.