Java 在 SpringMVC 中使用 @ResponseBody 返回 JsonObject

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19204048/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 15:00:59  来源:igfitidea点击:

Returning JsonObject using @ResponseBody in SpringMVC

javajsonspring-mvc

提问by dade

I am using the new Java API (JSR 353) for JSON in a SpringMVC project.

我在 SpringMVC 项目中为 JSON 使用新的 Java API (JSR 353)。

The idea is to generate some piece of Json data and have it returned to the client. The controller I have look somewhat like this:

这个想法是生成一些 Json 数据并将其返回给客户端。我的控制器看起来有点像这样:

@RequestMapping("/test")
@ResponseBody
public JsonObject test() {
        JsonObject result = Json.createObjectBuilder()
                .add("name", "Dade")
                .add("age", 23)
                .add("married", false)
                .build();
        return result;
    }

And when I access this, instead of getting the expected representation of the JSON, I get these instead:

当我访问它时,我得到的不是 JSON 的预期表示,而是这些:

{"name":{"chars":"Dade","string":"Dade","valueType":"STRING"},"age":{"valueType":"NUMBER","integral":true},"married":{"valueType":"FALSE"}}

Why is this? What is going on? And how do I make it returned the expected JSON properly?

为什么是这样?到底是怎么回事?以及如何让它正确返回预期的 JSON?

采纳答案by Sotirios Delimanolis

The answer is pretty simple when you realize there is no special HandlerMethodReturnValueHandlerfor the new JSR 353 API. Instead, in this case, the RequestResponseBodyMethodProcessor(for @ResponseBody) uses a MappingHymanson2HttpMessageConverterto serialize the return value of your handler method.

当您意识到HandlerMethodReturnValueHandler新的 JSR 353 API没有什么特别之处时,答案非常简单。相反,在这种情况下,RequestResponseBodyMethodProcessor(for @ResponseBody) 使用 aMappingHymanson2HttpMessageConverter来序列化处理程序方法的返回值。

Internally, the MappingHymanson2HttpMessageConverteruses an ObjectMapper. By default, the ObjectMapperuses the getters of a class to serialize an object to JSON.

在内部,MappingHymanson2HttpMessageConverter使用ObjectMapper. 默认情况下,ObjectMapper使用类的 getter 将对象序列化为 JSON。

Assuming you are using Glassfish's provider implementation of the JSR 353, those classes are org.glassfish.json.JsonObjectBuilderImpl$JsonObjectImpl, org.glassfish.json.JsonStringImpl, and org.glassfish.json.JsonNumberImpl, and javax.json.JsonValue$3(an anonymous class for the value FALSE).

假设您正在使用GlassfishJSR 353 的提供程序实现,这些类是org.glassfish.json.JsonObjectBuilderImpl$JsonObjectImplorg.glassfish.json.JsonStringImpl、 和 org.glassfish.json.JsonNumberImpljavax.json.JsonValue$3(值的匿名类FALSE)。

Because JsonObjectImpl(your result, ie. root, object) is a Map(special type), ObjectMapperserializes the map's entries as JSON key-value pair elements, where the Map key is the JSON key, and the Map value is the JSON value. For the key, it works fine, serializing as name, age, and married. For the value, it uses the classes I mentioned above and their respective getters. For example, org.glassfish.json.JsonStringImplis implemented as

因为JsonObjectImpl(你的结果,即根,对象)是一个Map(特殊类型),ObjectMapper将映射的条目序列化为 JSON 键值对元素,其中 Map 键是 JSON 键,而 Map 值是 JSON 值。对于关键的,它工作正常,如序列化nameagemarried。对于值,它使用我上面提到的类及其各自的 getter。例如,org.glassfish.json.JsonStringImpl被实现为

final class JsonStringImpl implements JsonString {

    private final String value;

    public JsonStringImpl(String value) {
        this.value = value;
    }

    @Override
    public String getString() {
        return value;
    }

    @Override
    public CharSequence getChars() {
        return value;
    }

    @Override
    public ValueType getValueType() {
        return ValueType.STRING;
    }
    ...
}

ObjectMappertherefore uses the Java Bean getters to serialize the JsonStringImplobject (that is the Map Entry's value), as

ObjectMapper因此使用 Java Bean getter 来序列化JsonStringImpl对象(即 Map Entry 的值),如

{"chars":"Dade","string":"Dade","valueType":"STRING"}

The same applies for the other fields.

这同样适用于其他领域。

If you want to correctly write the JSON, simply return a String.

如果您想正确编写 JSON,只需返回一个String.

@RequestMapping("/test", produces="application/json")
@ResponseBody
public String test() {
        JsonObject result = Json.createObjectBuilder()
                .add("name", "Dade")
                .add("age", 23)
                .add("married", false)
                .build();
        return result.toString();
}

Or make your own HandlerMethodReturnValueHandler, a little more complicated, but more rewarding.

或者制作你自己的HandlerMethodReturnValueHandler,稍微复杂一点,但更有价值。

回答by Gary

The answer from Sotirios Delimanolis does indeed work, but in my case I had to ensure the proper HttpMessageConverter order was in place. This is because I needed to also convert JodaTime values to ISO 8601 format. This custom WebMvcConfigurerAdapter Configuration worked for me:

Sotirios Delimanolis 的答案确实有效,但就我而言,我必须确保正确的 HttpMessageConverter 顺序到位。这是因为我还需要将 JodaTime 值转换为 ISO 8601 格式。这个自定义的 WebMvcConfigurerAdapter 配置对我有用:

@Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {

@SuppressWarnings("UnusedDeclaration")
private static final Logger log = LoggerFactory.getLogger(WebConfiguration.class);

public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    log.info("Configuring Hymanson ObjectMapper");
    final MappingHymanson2HttpMessageConverter converter = new MappingHymanson2HttpMessageConverter();
    final ObjectMapper objectMapper = new ObjectMapper();

    //configure Joda serialization
    objectMapper.registerModule(new JodaModule());
    objectMapper.configure(com.fasterxml.Hymanson.databind.SerializationFeature.
            WRITE_DATES_AS_TIMESTAMPS, false);

    // Other options such as how to deal with nulls or identing...
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
    converter.setObjectMapper(objectMapper);

    StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
    /*
    StringHttpMessageConverter must appear first in the list so that Spring has a chance to use
     it for Spring RestController methods that return simple String. Otherwise, it will use
      MappingHymanson2HttpMessageConverter and clutter the response with escaped quotes and such
     */
    converters.add(stringHttpMessageConverter);
    converters.add(converter);
    super.configureMessageConverters(converters);
}
}