Explain Codes LogoExplain Codes Logo

Spring MVC - How to return simple String as JSON in Rest Controller

java
best-practices
json-handling
rest-controllers
Nikita BarsukovbyNikita Barsukov·Jan 24, 2025
TLDR

In Spring MVC, to return a String as JSON using a RestController, use the @RestController and @GetMapping annotations. The produces attribute of the @GetMapping annotation should be set to MediaType.APPLICATION_JSON_VALUE. Look at this quick example:

@RestController public class SimpleController { @GetMapping(value = "/stringAsJson", produces = MediaType.APPLICATION_JSON_VALUE) public String getStringAsJson() { return "{\"message\":\"Hello, World!\"}"; } }

This creates a JSON-formatted string when /stringAsJson is accessed. Thanks to @RestController, there's no need for @ResponseBody.

But wait! It gets better – no need for manually adding quotes anymore. Let's dive into the comprehensive solutions.

Best Practices for JSON String Response

Wrapping a String in an Object

Get classy and avoid manual quote escapes:

@RestController public class ClassResponseController { @GetMapping("/classStringAsJson") public Response getMessage() { return new Response("When life gives you classes, make objects."); } static class Response { private final String message; public Response(String message) { this.message = message; } public String getMessage() { return this.message; } } }

Fancy ResponseEntity

With ResponseEntity, gain extra control:

@RestController public class ResponseEntityController { @GetMapping("/responseEntityAsString") public ResponseEntity<String> getResponseEntityAsString() { return ResponseEntity.ok() .contentType(MediaType.APPLICATION_JSON) .body(JSONObject.quote("When life gives you lemons, JSONify them!")); } }

Here JSONObject.quote() ensures its input turns into a valid JSON text.

JSONify it with Collections.singletonMap

Perfect for a one-off key-value JSON object:

@RestController public class SingletonMapController { @GetMapping("/singletonMap") public Map<String, String> getSingletonMap() { return Collections.singletonMap("message", "Hello, Singleton World!"); } }

Delve Deeper with Advanced Configuration

To take JSON conversion into our own hands:

@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { // Sorting hat code to choose Jackson converter over String converter } }

Mastering the Art of JSON Handling

RestyGWT Compatibility

Work with RestyGWT? Make sure the returned JSON is appetizing:

@GetMapping(value = "/restyGwtFriendly", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<Object> getRestyGWTCompatibleString() { return ResponseEntity.ok(new JSONObject(Map.of("response", "Respecting the Rest(yGWT)!"))); }

Debugging Served on a Platter

Just like ketchup to fries, a logger nicely accompanies debugging:

private static final Logger logger = LoggerFactory.getLogger(YourController.class); @GetMapping("/loggableStringAsJson") public Map<String, String> getLoggableStringAsJson(HttpServletRequest request) { logger.info("Received order from {}", request.getRemoteAddr()); return Collections.singletonMap("response", "Order up! Hello, World!"); }

Handle Content with Elegance

Give your content negotiation a VIP upgrade:

@GetMapping("/negotiatedStringAsJson") public ResponseEntity<String> getNegotiatedStringAsJson() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return new ResponseEntity<>("{\"message\":\"The Art of Negotiation, JSON style!\"}", headers, HttpStatus.OK); }

Taking the JSON Path with Custom Conversion

Snap StringHttpMessageConverter out of default conversion for pure JSONness:

@Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.removeIf(httpMessageConverter -> httpMessageConverter instanceof StringHttpMessageConverter); // Configure your mighty Jackson converter }