13

I have the following method:

@RequestMapping(value = "/app/write", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
public
@ResponseBody
Status writeBuildData(@RequestBody JsonObject templateDataJSON){}

Basically I make an Ajax POST request sending JSON, I always get an empty JsonObject {} as a result

JsonObject templateDataJSON = "{}";

But if I use String instead of JsonObject, I get the correct value.

This app is made with Spring Mvc 4.1.4.

Dependencies:

compile 'org.codehaus.jackson:jackson-mapper-asl:1.9.13'
compile 'com.google.code.gson:gson:2.3.1'

Any idea what I am missing and why the JsonObject doesn't get injected and always gives me {}?

Mythul
  • 1,807
  • 7
  • 34
  • 53

5 Answers5

14

Spring no longer supports Jackson 1 as a message converter implementation.

So your

compile 'org.codehaus.jackson:jackson-mapper-asl:1.9.13'

is actually meaningless to Spring.

Your

compile 'com.google.code.gson:gson:2.3.1'

will cause Spring to use GsonHttpMessageConverter and, basically, do

String json = "{\"random\":\"42\"}";
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson(json, JsonObject.class);

JsonObject is a Gson type. Gson is aware of it and knows how to deserialize JSON object json into it. This will work correctly and will generate a JsonObject which has a value of

{"random":"42"}

Since you're saying that you're getting an empty JsonObject, I can only assume that you have Jackson 2 on your classpath.

Spring registers the Jackson HttpMessageConverter, MappingJackson2HttpMessageConverter, before the GsonHttpMessageConverter if both are present on the classpath.

With Jackson, Spring would have deserialized your request body basically as such

ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JsonObject jsonObject = mapper.readValue(json, JsonObject.class);

which you'll note results in

{}

This is because Jackson knows nothing about the type JsonObject so it has to dynamically build a deserialization strategy. That strategy depends on properties which Jackson defines as setters (for the context of deserialization) or anything annotated with @JsonProperty, which obviously JsonObject doesn't have. So it basically thinks the type JsonObject doesn't have any properties (or maybe none that appear in your custom JSON content). As such, and because it ignores any unknown properties (which would have caused it to throw exceptions), it simply returns a new, empty JsonObject object.

One solution is to remove Jackson 2 from the classpath. Another solution is to explicitly add HttpMessageConverter instances in the order you want.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • Would it be possible to change ObjectMapper behavior to not ignore unknown values? – Rafael Dec 14 '18 at 11:06
  • @Rafael Yeah, you can register a `MappingJackson2HttpMessageConverter` yourself where you initialize the `ObjectMapper` however you want it. With Spring Boot, this might be even easier by just declaring an `ObjectMapper` `@Bean`. – Sotirios Delimanolis Dec 14 '18 at 17:08
8

The explanation why it fails is perfectly done at Sotirios Delimanolis answer.

However there is a workaround:

@RequestBody Map<String, String> json

That way you can continue using Jackson HttpMessageConverter and work with custom objects in payload.

Rafael
  • 2,521
  • 2
  • 33
  • 59
2

It is quite simple

@PostMapping
public ResponseEntity<?> yourMethod(@RequestBody String data) {
    JSONObject jsonObject = new JSONObject(data);
    //use the jsonObject now
}
jfk
  • 4,335
  • 34
  • 27
0

What represents JsonObject ? You should use object that represent json you sending. Something like

public class Foo {
    String foo;
    String bar;
}

instead JsonObject

and json like :

{
  "foo" : "val",
  "bar" : "val"
}
Dawid D
  • 111
  • 3
  • JsonObject is the default class from Gson. I wanted to use it in order to avoid creating other classes for modelling. – Mythul May 21 '15 at 20:16
-2

Have you defined a dedicated converter for it ?

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-requestbody

ABalhier
  • 72
  • 2