There is a clean way to return string as json in a Spring Web API?

坚强是说给别人听的谎言 提交于 2021-01-24 08:12:51

问题


For example, I had to implement as below:

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

When the action is called by Ajax in a JS file, the response received is: HelloWorld. So, if the Ajax request is configured to only receive json encoded responses, I receive the standard deconding error. To solve this problem in server-side, I need to receive "HelloWorld".

My question is: There is a clean way I can do this, instead of just changing the string returned for the string below?

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

UPDATE NOTE: In the time of this question, I was satisfied with the solution of returning a JSON map instead of a single string, as I wanted previously.

But now, I've spent some time searching about this, and trying to understand more about JSON patterns.

First of all, I found that my question is duplicated of these questions one, two and three. The correct answer says that the problem is with Spring Boot's default serializer (Jackson's library) that treats a string value (when serializing) as a raw JSON string, so it returns the value without double quotes instead of adding, as I was expecting.

And to be fair, I will choose the Bhargav's answer, that reached closer to what I was looking for. The answers from ddbullfrog and RohaNayak are correct, but they do not solve my problem correctly. Ademir Constantino's comment is correct too, he says to use plain/text instead of json format, using this reference. The Luay Absulraheem's answer is a correct way to config the action, but it doesn't work the way wanted, it continue to send the string as raw json. Thank you very much for your answers!

Additionally, it is possible to customize the Spring Boot to use Gson as its serializer, as can be seen on this article. After installing Gson dependency on your project, you just have to add this line on your application.properties file:

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

In this way, you don't have to parse every time you want to send a string value to be serialized by Spring Boot and don't receive it without double quotes on your AJAX handler.


I was trying to achieve the same behavior I get on ASP.NET MVC, which used the JSON.NET library to perform serialization. This and the fact that AJAX accepts a single string (e.g. "hello world") as a JSON, have made me do a deeper research about JSON patterns.

There is more than one "standard" definition of a JSON format. As it's discussed on this Stack's other question, the current "INTERNET STANDARD" from JSON values is defined by RFC-8259 as it is said on page 5 of the document:

A JSON value MUST be an object, array, number, or string, or one of the following three literal names:

  • false
  • null
  • true

As can be seen above, a JSON can be one wider range of data types than before in older standards.


UPDATE NOTE: Another alternative is to return a char[] instead of a String instance. In this way, Spring Boot will not recognize as raw JSON string:

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

回答1:


Yes,you can use libraries like gson or jackson to convert your strings into JSON, there by giving you a json output like below.

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

Also, dont forget to add produces = MediaType.APPLICATION_JSON_VALUE to the method exposed so that spring knows what kind of o/p needs to be produced.




回答2:


Try adding produces = {MediaType.APPLICATION_JSON_VALUE } to your @RequestMapping, as it affects the actual content type written. To produce a JSON response with UTF-8 encoding, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE } should be used.




回答3:


Solution

There is a StringHttpMessageConverter configured by default in Spring that handles any Strings returned from controllers regardless of the media type as if it were text/plain.

You can sort this out by moving StringHttpMessageConverter after all other message converters configured in Spring, or replacing it with your own implementation that does not handle */* media type and configuring it up.

In Spring boot: Extend WebMvcConfigurerAdapter, override extendMessageConverters and modify the received list as you see fit, e.g.

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

After this, Jackson (and possibly other JSON serializers) will get their say in the results handling and serialize My string as "My string" in the response.


Background

When processing a result into HTTP response body, Spring iterates through its registered HttpMessageConverters and chooses the first that matches the Java result's type and the respective media type (e.g. from @RequestMapping::produces).

In this case, the returned result type from controller is String and the media type is application/json or similar.

In the default configuration, there is a message converter called StringHttpMessageConverter which handles String return types and media types text/plain and */*. As it is generally registered before any other serializer you would use to handle json data (e.g. Jackson), it will just simply translate any String to its identity and the result conversion is done, writing unquoted, unescaped string to the response body as if it was text/plain, regardless of our application/json media type.

So the options we have:
(a) Take over the creation of message converters and supply a different order or a StringHttpMessageConverter that does not accept */*.
(b) Take over the message converter selection logic and see if there are more specific matches than */* before choosing a converter.
(c) Somehow hack into the created converters and replace/change/remove StringHttpMessageConverter

For option (a), this is impractical, because
1. StringHttpMessageConverter always uses media type */*. It would be necessary to roll a custom implementation.
2. WebMvcConfigurationSupport::addDefaultMessageConverters is final and cannot be overriden to choose different converters.
3. Another configuration framework in use (e.g. Spring Boot) may use its own logic to supply the message converters. For example, inheriting and using WebMvcConfigurationSupport via dependency injection (e.g. using @Configuration) causes Spring Boot auto-configuration for MVC to stop.
It may be feasible to override the message converters configuration in the configuration framework. My search with Spring Boot went only as far as HttpMessageConverters class which supports ignoring the default converters so that could be a way to do this as well.

Option (b) is impractical too. The whole iteration logic is done in AbstractMessageConverterMethodProcessor::writeWithMessageConverters which unfortunately does a load of other things as well so there is no easy way overriding it.

So we arrive at option (c) which is outlined as the solution the start of this answer.




回答4:


Just create a map with key value. It will serve the purpose. Every JSON needs a key and a value map.put("hello":"your string value")

return map;




回答5:


You can return a json format String value to the client side;

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



来源:https://stackoverflow.com/questions/44317740/there-is-a-clean-way-to-return-string-as-json-in-a-spring-web-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!