【问题标题】:There is a clean way to return string as json in a Spring Web API?有一种干净的方法可以在 Spring Web API 中将字符串作为 json 返回吗?
【发布时间】:2017-11-03 04:54:42
【问题描述】:

例如,我必须实现如下:

@RequestMapping(value = "/get-string", method = {RequestMethod.GET})
public @ResponseBody String getString() {
    return "Hello World!";
}

Ajax 在 JS 文件中调用该动作时,收到的响应为:HelloWorld。因此,如果将 Ajax 请求配置为仅接收 json 编码响应,我会收到标准解码错误。为了在服务器端解决这个问题,我需要接收"HelloWorld"

我的问题是:有一种干净的方法可以做到这一点,而不仅仅是更改为下面的字符串返回的字符串?

    ...
    return "\"Hello World!\"";
}

更新说明:在提出这个问题时,我对返回 JSON 映射而不是我之前想要的单个字符串的解决方案感到满意。

但现在,我花了一些时间搜索这方面的内容,并尝试更多地了解 JSON 模式。

首先,我发现我的问题与onetwothree 这些问题重复。正确的answer 表示问题出在 Spring Boot 的默认序列化程序(Jackson 的库),它将字符串值(序列化时)视为原始 JSON 字符串,因此它返回的值没有双引号而不是添加,正如我所期待的.

公平地说,我会选择 Bh​​argav 的answer,它更接近我所寻找的。 ddbullfrogRohaNayak 的答案是正确的,但它们并不能正确解决我的问题。 Ademir Constantino 的 comment 也是正确的,他说使用纯文本而不是 json 格式,使用这个 reference。 Luay Absulraheem 的answer 是配置操作的正确方法,但它不能按预期方式工作,它继续将字符串作为原始 json 发送。 非常感谢您的回答!

此外,可以自定义 Spring Boot 以使用 Gson 作为其序列化程序,如article 所示。在您的项目上安装 Gson 依赖项后,您只需在 application.properties 文件中添加这一行:

...
spring.http.converters.preferred-json-mapper=gson

这样就不用每次要发送字符串值时都要解析 由 Spring Boot 序列化,并且在 AJAX 处理程序上没有双引号时不会接收它。


我试图在 ASP.NET MVC 上实现相同的行为,它使用 JSON.NET library 来执行序列化。这一点以及 AJAX 接受单个字符串(例如“hello world”)作为 JSON 的事实,让我对 JSON 模式进行了更深入的研究。

JSON 格式有多个“标准”定义。正如在此 Stack 的其他 question 中所讨论的,JSON 值中的当前“互联网标准”由 RFC-8259 定义,正如文档第 5 页所述:

JSON 值必须是对象、数组、数字或字符串,或其中之一 以下三个字面名称:

  • 是的

如上所示,JSON 可以是比以前的旧标准中更广泛的数据类型。


更新说明:另一种选择是返回 char[] 而不是 String 实例。这样,Spring Boot 将不会识别为原始 JSON 字符串:

@RequestMapping(value = "/get-string", method = {RequestMethod.GET})
public @ResponseBody char[] getString() {
    return "Hello World!".toCharArray();
}

【问题讨论】:

标签: java json spring string serialization


【解决方案1】:

是的,您可以使用 gson 或 jackson 之类的库将您的字符串转换为 JSON,然后为您提供如下所示的 json 输出。

Gson gson= new GsonBuilder().create();
    gson.toJson(Your String);

另外,别忘了添加 produces = MediaType.APPLICATION_JSON_VALUE暴露的方法让spring知道需要产生什么样的o/p。

【讨论】:

  • 正如对问题的解释,所需的行为是序列化控制器操作返回的字符串。此解决方案在返回字符串之前执行序列化时有效。这是必要的,因为 Spring Boot 序列化程序的默认行为是将字符串作为原始 Json 字符串而不是单个值(例如“hello world”)来处理。
【解决方案2】:

尝试将produces = {MediaType.APPLICATION_JSON_VALUE } 添加到您的@RequestMapping,因为它会影响写入的实际内容类型。要使用 UTF-8 编码生成 JSON 响应,应使用 produces = {MediaType.APPLICATION_JSON_UTF8_VALUE }

【讨论】:

    【解决方案3】:

    解决方案

    在 Spring 中默认配置了一个 StringHttpMessageConverter,它处理从控制器返回的任何 Strings,无论媒体类型如何,就好像它是 text/plain

    您可以通过在 Spring 中配置的所有其他消息转换器之后移动 StringHttpMessageConverter 来解决此问题,或者将其替换为您自己的不处理 */* 媒体类型的实现并进行配置。

    在春季启动: 扩展 WebMvcConfigurerAdapter,覆盖 extendMessageConverters 并根据需要修改收到的列表,例如

    @Configuration
    public class StringHttpMessageConverterReorganizingWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter
    {
        @Override
        public void extendMessageConverters(final List<HttpMessageConverter<?>> converters)
        {
            super.extendMessageConverters(converters);
    
            // do whatever you see fit with StringHttpMessageConverter in the converters list
            // ...
        }
    }
    

    此后,Jackson(可能还有其他 JSON 序列化程序)将在结果处理中发表意见,并将响应中的 My string 序列化为 "My string"


    背景

    在将结果处理为 HTTP 响应正文时,Spring 会遍历其注册的 HttpMessageConverters,并选择与 Java 结果类型和相应媒体类型匹配的第一个(例如来自 @RequestMapping::produces)。

    在这种情况下,控制器返回的结果类型为String,媒体类型为application/json或类似。

    在默认配置中,有一个名为StringHttpMessageConverter 的消息转换器处理String 返回类型和媒体类型text/plain*/*。由于它通常在您用于处理 json 数据(例如 Jackson)的任何其他序列化程序之前注册,因此它只会简单地将任何 String 转换为其标识并完成结果转换,将未引用的、未转义的字符串写入响应正文为如果是text/plain,不管我们的application/json 媒体类型。

    所以我们有以下选择:
    (a) 接管消息转换器的创建并提供不接受*/* 的不同订单或StringHttpMessageConverter
    (b) 接管消息转换器选择逻辑,在选择转换器之前查看是否有比*/* 更多的特定匹配项。
    (c) 以某种方式侵入创建的转换器并替换/更改/删除StringHttpMessageConverter

    对于选项 (a),这是不切实际的,因为
    1.StringHttpMessageConverteralways uses media type */*。有必要推出自定义实现。
    2.WebMvcConfigurationSupport::addDefaultMessageConvertersis final and cannot be overriden to choose different converters.
    3. 另一个正在使用的配置框架(例如 Spring Boot)可能会使用自己的逻辑来提供消息转换器。例如,通过依赖注入继承和使用WebMvcConfigurationSupport(例如使用@Configuration)会导致MVC的Spring Boot自动配置停止。
    在配置框架中覆盖消息转换器配置可能是可行的。我使用 Spring Boot 进行的搜索仅限于支持忽略默认转换器的 HttpMessageConverters 类,因此这也是一种方法。

    选项 (b) 也是不切实际的。整个迭代逻辑在 AbstractMessageConverterMethodProcessor::writeWithMessageConverters 中完成,不幸的是它还做了很多其他事情,所以没有简单的方法覆盖它。

    因此我们得出了选项 (c),该选项被概述为该答案的开头的解决方案。

    【讨论】:

      【解决方案4】:

      只需创建一个带有键值的地图。它将达到目的。 每个 JSON 都需要一个键和一个值 map.put("hello":"你的字符串值")

      返回地图;

      【讨论】:

        【解决方案5】:

        可以向客户端返回一个json格式的String值;

        return "{\"response\":\"Hello World!\"}";

        【讨论】:

        • 这是一种方式...但我只想返回"Hello World!"。正如 JSON 规范所说 (json.org),“字符串是零个或多个 Unicode 字符的序列,用双引号括起来,使用反斜杠转义。”可以是 JSON。
        • 组成 JSON 的两个主要部分是键和值。它们一起构成一个键/值对。 link Key: 键始终是用引号括起来的字符串。 值: 值可以是字符串、数字、布尔表达式、数组或对象。所以,我认为没有密钥它只是一个字符串而不是 JSON。
        • 我相信你不能返回一个 ResponseBody 对象。检查这个stackoverflow.com/questions/8951534/… 它可以通过返回纯文本字符串来解决您的问题。
        猜你喜欢
        • 2014-02-28
        • 2017-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-02
        • 1970-01-01
        相关资源
        最近更新 更多