How Spring Boot Chooses Response Formats and Customizes JSON, XML, and YAML Output
This article explains Spring Boot's default JSON response, the internal HttpMessageConverter selection process, and how to configure XML and custom YAML responses by adding Jackson dependencies and implementing a custom HttpMessageConverter.
Environment: Spring Boot 3.0.5
Message Format Conversion Principle
By default a @RestController returns JSON. Example controller:
<code>@RestController
@RequestMapping("/rmf")
public class ResponseMessageFormatController {
@GetMapping("/index")
public Users index() {
return new Users(1, "张飒", 66, "男");
}
}
</code>When no Accept header is sent, Spring treats the request as accepting any media type ( */* ), iterates over all HttpMessageConverter s, and selects the first concrete MediaType that is not a wildcard. application/json is first, so JSON is returned via MappingJackson2HttpMessageConverter .
Core source code that performs the selection:
<code>public abstract class AbstractMessageConverterMethodProcessor {
protected <T> void writeWithMessageConverters(...) {
List<MediaType> acceptableTypes;
try {
// get Accept header, default */*
acceptableTypes = getAcceptableMediaTypes(request);
}
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
List<MediaType> compatibleMediaTypes = new ArrayList<>();
determineCompatibleMediaTypes(acceptableTypes, producibleTypes, compatibleMediaTypes);
for (MediaType mediaType : compatibleMediaTypes) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
} else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter) converter : null);
if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) converter.getClass(), inputMessage, outputMessage);
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
}
}
}
</code>Returning XML format
To enable XML, add the Jackson XML dependency:
<code><dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
</code>After adding the dependency, setting Accept: application/xml makes Spring automatically use MappingJackson2XmlHttpMessageConverter to produce XML.
Custom Message Format (YAML)
To support YAML, add the Jackson YAML dependency:
<code><dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
</code>Create a custom HttpMessageConverter for the application/yaml media type:
<code>@Component
public class YamlHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
public YamlHttpMessageConverter() {
super(new MediaType("application", "yaml"));
}
@Override
protected boolean supports(Class<?> clazz) {
return true;
}
@Override
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
protected void writeInternal(Object t, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
try (OutputStream os = outputMessage.getBody()) {
YAMLFactory factory = new YAMLFactory();
// Remove leading "---"
factory.configure(Feature.WRITE_DOC_START_MARKER, false);
ObjectMapper mapper = new ObjectMapper(factory);
os.write(mapper.writeValueAsString(t).getBytes(StandardCharsets.UTF_8));
}
}
}
</code>Testing with Postman and Accept: application/yaml returns the correct YAML representation.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.