Spring boot how to return json embedded in object structure
You can use AbstractHttpMessageConverter
for custom mapping operations for your problem.
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
@Component
public class CustomMessageConverter extends AbstractHttpMessageConverter<Map<String, Map<String, Object>>> {
public CustomMessageConverter() {
super(MediaType.APPLICATION_JSON);
}
@Override
protected boolean supports(Class<?> clazz) {
return HashMap.class.isAssignableFrom(clazz);
}
@Override
protected Map<String, Map<String, Object>> readInternal(Class<? extends Map<String, Map<String, Object>>> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
protected void writeInternal(Map<String, Map<String, Object>> value, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
PrintStream printStream = new PrintStream(outputMessage.getBody());
printStream.print("{");
// track to put comma between items
boolean printedEntry = false;
for (Map.Entry<String, Map<String, Object>> entry : value.entrySet()) {
/* // if you want to disable null print uncomment it
if (entry.getValue() == null) {
continue;
}
*/
if (printedEntry) {
printStream.print(",");
}
printStream.print("\"" + entry.getKey() + "\":");
if (entry.getValue() == null) {
printStream.print("null");
continue;
}
// INNER ENTRY LOOP
printStream.print("{");
// track to put comma between items
boolean printedInnerEntry = false;
for (Map.Entry<String, Object> innerEntry : entry.getValue().entrySet()) {
if (printedInnerEntry) {
printStream.print(",");
}
printStream.print("\"" + innerEntry.getKey() + "\":");
printStream.print(innerEntry.getValue());
printedInnerEntry = true;
}
printStream.print("}");
printedEntry = true;
}
printStream.print("}");
}
}
Test@RestController
public class HomeController {
@GetMapping
public ResponseEntity<Map<String, Map<String, Object>>> getComplexStructure() {
Map<String, Map<String, Object>> result = new HashMap<>();
Map<String, Object> innerItem1 = new HashMap<>();
innerItem1.put("bar1", "{\"item\": 12, \"list\": [1, 2]}");
innerItem1.put("bar2", "{\"item\": 22, \"test\": \"test@\"}");
result.put("foo1", innerItem1);
Map<String, Object> innerItem2 = new HashMap<>();
innerItem2.put("tail1", "{\"item\": 55}");
innerItem2.put("tail2", "{\"item\": 77}");
result.put("foo2", innerItem2);
return ResponseEntity.ok(result);
}
}
GET http://localhost:8080/
Output
{"foo1":{"bar1":{"item": 12, "list": [1, 2]},"bar2":{"item": 22, "test": "test@"}},"foo2":{"tail1":{"item": 55},"tail2":{"item": 77}}}
CautionI recommend that create a new class for operating this special case. Because HashMap
is generic and caught all unrelated returns extended from HashMap
. So, if you want to prefer this way, you can change below codes.
Create a new return type from HashMap<String, Map<String, Object>>
public class CustomMessage extends HashMap<String, Map<String, Object>> {
}
In CustomMessageConverter
change supports
method with below.
@Override
protected boolean supports(Class<?> clazz) {
return CustomMessage.class.isAssignableFrom(clazz);
}
Also, you can change AbstractHttpMessageConverter<CustomMessage>
instead of AbstractHttpMessageConverter<Map<String, Map<String, Object>>>
but it doesn't required.
Change return type with this new class.
public ResponseEntity<CustomMessage> getComplexStructure() {
CustomMessage result = new CustomMessage();
...
return ResponseEntity.ok(result);
}
why is Spring Boot returning a string instead of JSON
While you were right about single quotes, you can achieve the JSON response without using DTO if you don't want to. You can try this:
@PostMapping("/api/signup")
public ResponseEntity signup(HttpServletRequest request, HttpServletResponse response) {
return ResponseEntity
.status(<http_status>)
.contentType(MediaType.APPLICATION_JSON)
.body("{\"status\":\"fail\", \"message\":\"foo\"}");
}
Related Topics
How to Use Variables in One Method into Another Method
Rounding to the Nearest Hundered-Thousandths
How to Sort Date in Descending Order Using Comparator
Spring Rest - Create Zip File and Send It to the Client
How to Get Summation of Pair(Key) Values in a Map in Java
On Selenium Webdriver How to Get Text from Span Tag
Java.Io.Filenotfoundexception: (Access Is Denied)
Disable Quick Settings Tile in Android
The Request Was Rejected Because No Multipart Boundary Was Found in Springboot
Jparepository Findall() Returns Empty Result
How to Prevent Duplicate Results in Hibernate
How to Find Distinct Rows With Field in List Using JPA and Spring
How to Store a String Longer Than 4000 Characters in an Oracle Database Using Java/Jdbc
Spring Rest Post Json Requestbody Content Type Not Supported