7

I have a Spring MVC controller with the following method:

@RequestMapping(value = "/stringtest", method = RequestMethod.GET)
public String simpletest() throws Exception {
    return "test";
}

This sits inside a controller that starts like this:

@RestController
@RequestMapping(value = "/root")
public class RootController

When I call other methods that return objects, those objects are serialized by Jackson into JSON. But this method which returns a String is not converted to JSON. In case it's not clear, here's an example using curl:

$curl http://localhost:8080/clapi/root/stringtest 
test

So the problem is that "test" without any quotes is not a JSON string, but my REST client is expecting a string. I expected the curl command to show that string with quotes around it so it's legal JSON instead:

"test"

I am using Spring WebMVC 4.1.3 and Jackson 2.4.3. I have tried adding a "produces" attribute to the RequestMapping to say it should return JSON. In this case, the Content-Type attribute sent back is "application/json" but still the test string is not quoted.

I could workaround this by calling a JSON library to convert by Java String to JSON, but it seems like Spring MVC and Jackson generally do this automatically. Yet somehow they are not doing it in my case. Any ideas what I might have configured wrong to be getting just test back instead of "test"?

4
  • You could try wrapping your String in an Object. Otherwise, there is no way for Jackson to know what name to give to the JSON key of your String. Commented May 13, 2015 at 8:04
  • @Matt a string alone is valid JSON, it doesn't need to be an object. For example, even an array works fine. If I change the code to return an array, then I get ["test"]. So I expect if I return a simple string I should get "test" Commented May 13, 2015 at 19:09
  • This is not so obvious, look at this answer : stackoverflow.com/a/7487892/369946. According to the RFC definition of the "application/json" media type, the JSON should be an object or an array. Commented May 15, 2015 at 8:18
  • @Matt Hmm, I agree. That page does provide some good opposing viewpoints. Its seems that the RFC and ECMA-404 do not agree, as ECMA-404 gives no such limitation. So if you follow the RFC then JSON has to start with { or [, but not so with the ECMA-404 spec. When testing in Javascript, both the built-in JSON.stringify("test") (tested on Chrome and Safari) and AngularJS's angular.toJson("test") produce "test" as output so both the Javascript web implementations and Angular believe that a quoted string is valid JSON. Still, would be nice if the browsers and the RFC agreed with each other! Commented May 16, 2015 at 17:58

3 Answers 3

10

It turns out that when you use the @EnableWebMvc annotation that it turns on a bunch of http message converters by default. The second one in the list is the StringHttpMessageConverter which the documentation says will be applied for text/* content types. However, after stepping through with the debugger, it applies to String objects for */* content types- which obviously includes application/json.

The MappingJackson2HttpMessageConverter which is responsible for application/json content types is further down on this list. So for Java objects other than String, this one gets called. That's why it was working for Object and Array types, but not String- despite the good suggestions of using the produces attribute to set application/json content type. Although that content type is necessary to trigger this converter, the String converter was grabbing the job first!

As I was extending the WebMvcConfigurationSupport class for some other configuration, I overrode the following method to put the Jackson converter first so when the content-type is application/json then this one will be used instead of the String converter:

@Override
protected void configureMessageConverters(
        List<HttpMessageConverter<?>> converters) {
    // put the jackson converter to the front of the list so that application/json content-type strings will be treated as JSON
    converters.add(new MappingJackson2HttpMessageConverter());
    // and probably needs a string converter too for text/plain content-type strings to be properly handled
    converters.add(new StringHttpMessageConverter());
}

Now when I call the test method from curl I get the desired "test" output instead of just test, so the angular client which is expecting JSON is now happy.

Sign up to request clarification or add additional context in comments.

Comments

1

Try this:

@RequestMapping(value = "/stringtest", method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_VALUE)

1 Comment

Thanks Shyam, I had tried that but it just sets the Content-Type to "application/json" but doesn't wrap my string in quotes. I did try cutting and pasting your suggestion in case I typed it wrong, but it doesn't work.
0

Try something like this

@RequestMapping(value = "/stringtest", method = RequestMethod.GET, produces="application/json")
public @ResponseBody String simpletest() throws Exception {
    return "test";
}

1 Comment

Thanks Martin, I had tried that produces attribute, but as I mentioned all that seems to do is change the Content-Type to "application/json" but still the output is test with no quotes. And the @ResponseBody is not necessary because I have the @RestController annotation which makes that automatic. Still, I cut'n'paste your suggestion in case I was wrong about that but no luck.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.