Configuring Custom Converters and Content Negotiation in Spring Boot

Spring Boot applications often require handling diverse data formats during request processing and response generation. By implementing custom converters and configuring content negotiation strategies, developers can insure seamless data exchange between clients and servers regardless of the preferred media type.

Implementing Custom Data Converters

When a client submits form data, Spring Boot utilizes built-in converters to bind request parameters to Java objects. While the framework provides over a hundred default converters, specific business requirements often necessitate custom conversion logic.

Defining the Conversion Logic

Consider a scenario where a vehicle description is submitted as a single string containing the model name and price, separated by a comma. To map this string to a Vehicle object, a custom converter must be registered.

Input Format Example:

<!-- Form input submitting a combined string -->
Vehicle Info: <input name="vehicleData" value="Storm Cruiser,85000.50"><br/>

Registering the Converter

Create a configuration class to register the custom converter with the FormatterRegistry. This involves implementing the WebMvcConfigurer interface.

package com.example.demo.config;

import com.example.demo.model.Vehicle;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;

@Configuration
public class MvcRegistryConfig {

    @Bean
    public WebMvcConfigurer mvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addFormatters(FormatterRegistry registry) {
                // Register a converter that transforms String input into a Vehicle object
                registry.addConverter(new Converter<String, Vehicle>() {
                    @Override
                    public Vehicle convert(String rawInput) {
                        if (!StringUtils.hasText(rawInput)) {
                            return null;
                        }
                        
                        String[] parts = rawInput.split(",");
                        Vehicle model = new Vehicle();
                        
                        // Assign parsed values to the object properties
                        model.setModelName(parts[0].trim());
                        model.setCost(Double.parseDouble(parts[1].trim()));
                        
                        return model;
                    }
                });
            }
        };
    }
}

Converter Registration Details

The converters are stored internally within a ConcurrentHashMap. When debugging, you can observe the registered converters in the registry. If multiple converters are defined for the same source and target type pair, the last registered one will override previous definitions.

For instance, registering two converters for String to Vehicle will result in the second implementation replacing the first. This behavior allows for dynamic updates but requires careful management to avoid unintended overrides.

Handling JSON Responses

Spring Boot simplifies returning data in JSON format. When the web starter dependency is included, the necessary libraries (such as Jackson) are automatically configured.

Creating a Response Endpoint

Define a controller method that returns a Java object. By using the @ResponseBody annotation, the return value is serialized directly into the HTTP response body.

package com.example.demo.controller;

import com.example.demo.model.EntityRecord;
import com.example.demo.model.Vehicle;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Date;

@Controller
public class DataEndpoint {

    @GetMapping(value = "/fetchRecord")
    @ResponseBody
    public EntityRecord fetchRecord() {
        EntityRecord record = new EntityRecord();
        record.setIdentifier(500);
        record.setLabel("Admin User");
        record.setYearsActive(5);
        record.setRegistrationDate(new Date());
        record.setActiveStatus(true);
        
        Vehicle associatedVehicle = new Vehicle();
        associatedVehicle.setModelName("Luxury Sedan");
        associatedVehicle.setCost(45000.00);
        record.setPrimaryVehicle(associatedVehicle);
        
        return record;
    }
}

During execution, the AbstractJackson2HttpMessageConverter handles the serialization process. It utilizes a JsonGenerator to write the object fields into the output stream, which is then sent to the client.

Content Negotiation Strategies

Content negotiation allows the server to return different media types based on the client's capabilities, typically indicated by the Accept header in the HTTP request.

Mechanism Overview

  • If the client sends Accept: application/json, the server responds with JSON.
  • If the client sends Accept: application/xml, the server responds with XML.

Enabling XML Support

To support XML responses, add the Jackson XML dependency to the project configuration.

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.15.0</version>
</dependency>

Browser Behavior and Accept Headers

When accessing endpoints via a standard web browser, the response might default to XML instead of JSON. This occurs because browsers often send an Accept header prioritizing application/xhtml+xml or application/xml with a higher quality factor (q-value) than */*.

Example Header:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Since application/xml has a weight of 0.9 compared to 0.8 for others, Spring Boot selects the XML message converter.

Parameter-Based Negotiation

To override browser header behavior, enable content negotiation based on request parameters. This allows clients to specify the desired format via a query parameter.

Configuration:

spring:
  mvc:
    contentnegotiation:
      favor-parameter: true
      parameter-name: formatType

With this configuration, appending ?formatType=json to the URL forces a JSON response, while ?formatType=xml forces XML. The parameter name defaults to format but can be customized as shown above. Ensure the specified format corresponds to a registered HttpMessageConverter.

Tags: spring-boot java JSON content-negotiation converters

Posted on Sat, 19 Sep 2026 16:28:28 +0000 by designerguy