【问题标题】:Spring boot how to return json embedded in object structureSpring Boot如何返回嵌入在对象结构中的json
【发布时间】:2021-04-13 17:14:08
【问题描述】:

在我的 Spring-boot 控制器中,我有一个返回以下数据结构的端点:

public ResponseEntity<Map<String, Map<String, Object>>> getComplexStructure()

这个结构中的内部Object 被保存为数据库中的原始 JSON 字符串,我在我的应用程序中不使用实际值。目前我必须将该 JSON 反序列化为一个对象,以便我可以从函数中返回它,以便 Spring 可以再次将其序列化为 JSON。这听起来效率低下,我想知道我能否以某种方式跳过 Deserialize Reserialize 部分,只返回 Spring 的原始 JSON 以嵌入到它周围的序列化数据结构中。

现在显然如果我像这样返回字符串:

public ResponseEntity<Map<String, Map<String, String>>> getComplexStructure()

JSON 解析器会将字符串放在引号中并转义它,它只是一个字符串而不是 json 结构。

是否有一些注释或我可以做些什么来跳过该嵌入式 JSON 的不必要的序列化和反序列化?

读起来听上去很简单,如果这个过程有一些好的技术术语,也请提一下,这会让谷歌搜索更容易:)

【问题讨论】:

    标签: json spring-boot


    【解决方案1】:

    您可以使用AbstractHttpMessageConverter 为您的问题进行自定义映射操作。

    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("}");
        }
    }
    

    测试

    @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/

    输出

    {"foo1":{"bar1":{"item": 12, "list": [1, 2]},"bar2":{"item": 22, "test": "test@"}},"foo2":{"tail1":{"item": 55},"tail2":{"item": 77}}}
    

    注意

    我建议创建一个新类来操作这种特殊情况。因为HashMap 是通用的并且捕获了从HashMap 扩展的所有不相关的返回。所以,如果你想更喜欢这种方式,你可以改变下面的代码。

    HashMap&lt;String, Map&lt;String, Object&gt;&gt;创建一个新的返回类型

    public class CustomMessage extends HashMap<String, Map<String, Object>> {
    }
    

    CustomMessageConverter 中更改supports 方法如下。

    @Override
    protected boolean supports(Class<?> clazz) {
       return CustomMessage.class.isAssignableFrom(clazz);
    }
    

    此外,您可以更改 AbstractHttpMessageConverter&lt;CustomMessage&gt; 而不是 AbstractHttpMessageConverter&lt;Map&lt;String, Map&lt;String, Object&gt;&gt;&gt;,但这不是必需的。

    使用这个新类更改返回类型。

    public ResponseEntity<CustomMessage> getComplexStructure() {
       CustomMessage result = new CustomMessage();
       ...
       return ResponseEntity.ok(result);
    }
    

    【讨论】:

    • 不要错过Caution 部分。它对于仅映射您想要的方法很有用。
    • 我实际上是在写一篇关于如何防止所有 Hashmap 受到影响的评论,然后您添加了注意部分。所以我取消了我的评论:D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-04
    • 2020-06-05
    • 2021-03-09
    • 2020-02-11
    • 1970-01-01
    相关资源
    最近更新 更多