Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatic conversion of JSON form parameter in Spring MVC 4.0

I am trying to build a Spring MVC controller which will receive a POSTed form with a parameter in JSON format, and have Spring automatically convert it to a Java object.

  • Request content type is application/x-www-form-urlencoded
  • The name of the parameter that contains a JSON string is data.json

This is the controller:

@Controller
public class MyController {
    @RequestMapping(value = "/formHandler", method = RequestMethod.POST)
    public @ResponseBody String handleSubscription(
        @RequestParam("data.json") MyMessage msg) {
        logger.debug("id: " + msg.getId());
        return "OK";
    }
}

And this is what the MyMessage object looks like:

public class MyMessage {
    private String id;
    // Getter/setter omitted for brevity
}

Perhaps not surprisingly, posting a form with parameter data.json={"id":"Hello"} results in HTTP error 500 with this exception:

org.springframework.beans.ConversionNotSupportedException:
    Failed to convert value of type 'java.lang.String' to required type 'MyMessage' 
nested exception is java.lang.IllegalStateException:
    Cannot convert value of type [java.lang.String] to required type [MyMessage]: no matching editors or conversion strategy found

If I read the MappingJackson2HttpMessageConverter docs correctly, Jackson JSON conversion is triggered by Content-Type application/json, which I obviously cannot use since this is a form POST (and I don't control the POSTing part).

Is it possible to get Spring to convert the JSON string into an instance of MyMessage, or should I just give up, read it as a String and perform the conversion myself?

like image 715
Jon Ekdahl Avatar asked Dec 16 '13 22:12

Jon Ekdahl


People also ask

How do you pass a JSON object as a parameter in Java?

Either use an export mapping to create a JSON string that you can pass to the Java action and then create a JSON object again from that string or just pass a root object to the Java and then in Java retrieve all the attached objects over the references to that root object.

How pass JSON object in post request in spring boot?

Send JSON Data in POST Spring provides a straightforward way to send JSON data via POST requests. The built-in @RequestBody annotation can automatically deserialize the JSON data encapsulated in the request body into a particular model object. In general, we don't have to parse the request body ourselves.

What is an HttpMessageConverter?

HttpMessageConverter is a strategy interface that specifies a converter that can convert from and to HTTP requests and responses in Spring REST Restful web services. Internally Spring MVC uses it to convert the Http request to an object representation and back.


2 Answers

Spring invokes your @RequestMapping methods with reflection. To resolve each argument it's going to pass to the invocation, it uses implementations of HandlerMethodArgumentResolver. For @RequestParam annotated parameters, it uses RequestParamMethodArgumentResolver. This implementation binds a request parameter to a single object, typically a String or some Number type.

However, your use case is a little more rare. You rarely receive json as a request parameter, which is why I think you should re-think your design, but if you have no other choice, you need to register a custom PropertyEditor that will take care of converting the request parameter's json value into your custom type.

Registration is simple in an @InitBinder annotated method in your @Controller class

@InitBinder
public void initBinder(WebDataBinder dataBinder) {
    dataBinder.registerCustomEditor(MyMessage.class, new PropertyEditorSupport() {
        Object value;
        @Override
        public Object getValue() {
            return value;
        }

        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            value = new Gson().fromJson((String) text, MyMessage.class);
        }
    });
}

In this particular case, we don't need all the methods of the PropertyEditor interface, so we can use PropertyEditorSupport which is a helpful default implementation of PropertyEditor. We simply implement the two methods we care about using whichever flavor of JSON parser we want. I used Gson because it was available.

When Spring sees that it has a request parameter that you requested, it will check the parameter type, find the type MyMessage and look for a registered PropertyEditor for that type. It will find it because we registered it and it it will then use it to convert the value.

You might need to implement other methods of PropertyEditor depending on what you do next.

My recommendation is to never send JSON as a request parameter. Set your request content type to application/json and send the json as the body of the request. Then use @RequestBody to parse it.

like image 93
Sotirios Delimanolis Avatar answered Oct 16 '22 09:10

Sotirios Delimanolis


You can also use @RequestPart like this:

@RequestMapping(value = "/issues", method = RequestMethod.POST, headers = "Content-Type=multipart/form-data")
public String uploadIssue(@RequestParam("image") MultipartFile file, @RequestPart("issue") MyMessage issue)
like image 40
feht Avatar answered Oct 16 '22 11:10

feht