Methodargumentconversionnotsupportedexception When I Try to Map Json String Onto Java Domain Class in Spring Controller'S Method

MethodArgumentConversionNotSupportedException when I try to map json string onto java domain class in Spring controller's method

I think you need to specify that your GET mapping is looking to consume JSON:

@RequestMapping(value = "/get-templates", method = RequestMethod.GET, consumes = "application/json")
public List<Template> getTemplates(@RequestParam(required = false, name = "context") Context context) {
//...
}

If this doesn't work then you can call the Jackson ObjectMapper yourself:

@RequestMapping(value = "/get-templates", method = RequestMethod.GET)
public List<Template> getTemplates(@RequestParam(required = false, name = "context") String context) {
ObjectMapper mapper = new ObjectMapper();
Context myContext = mapper.readValue(context, Context.class);
//...
}

Cannot use Map as a JSON @RequestParam in Spring REST controller

Here is the solution that worked:
Just define a custom converter from String to Map as a @Component. Then it will be registered automatically:

@Component
public class StringToMapConverter implements Converter<String, Map<String, String>> {

@Override
public Map<String, Object> convert(String source) {
try {
return new ObjectMapper().readValue(source, new TypeReference<Map<String, String>>() {});
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
}

Meaning of parameters are transferred when backend receive the special character in springboot project

It seems it is getting URL encoded.

You could use URLDecoder.decode() on what you receive.

Jquery serialize & @RequestBody not working

The HTTP 400 Bad Request (The request sent by the client was syntactically incorrect) error occurs when the client did not send a syntactically correct entity.

In this case, the request body is expected to be a valid json, but it wasn't. As @Dave mentioned, your request does not contain a JSON, but a urlencoded string such as name=stack&value=overflow. Instead, it should be a JSON, such as {"name":"stack", "value":"overflow"}.

To achieve this, please refer to another good thread : Convert form data to JavaScript object with jQuery .

A possible fix of your code:

$("#add_more_contact").click(function(){
var formJson = $("#addContactForm").serializeToObject(); // use plugin, or build by yourself
// this variable should be a javascript object,
// such as {"name":"stack", "value":"overflow"}

$.ajax( {
url : "/myproject/admin/addContacts.htm",
type : "POST",
data : JSON.stringify(formJson), // serialize javascript object to JSON 'string'
dataType : "json", // 'text' -> 'json'
contentType : "application/json", // this can be omitted
success : ...,
error : ...,
});
});

Using Spring MVC Test to unit test multipart POST request

Since MockMvcRequestBuilders#fileUpload is deprecated, you'll want to use MockMvcRequestBuilders#multipart(String, Object...) which returns a MockMultipartHttpServletRequestBuilder. Then chain a bunch of file(MockMultipartFile) calls.

Here's a working example. Given a @Controller

@Controller
public class NewController {

@RequestMapping(value = "/upload", method = RequestMethod.POST)
@ResponseBody
public String saveAuto(
@RequestPart(value = "json") JsonPojo pojo,
@RequestParam(value = "some-random") String random,
@RequestParam(value = "data", required = false) List<MultipartFile> files) {
System.out.println(random);
System.out.println(pojo.getJson());
for (MultipartFile file : files) {
System.out.println(file.getOriginalFilename());
}
return "success";
}

static class JsonPojo {
private String json;

public String getJson() {
return json;
}

public void setJson(String json) {
this.json = json;
}

}
}

and a unit test

@WebAppConfiguration
@ContextConfiguration(classes = WebConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class Example {

@Autowired
private WebApplicationContext webApplicationContext;

@Test
public void test() throws Exception {

MockMultipartFile firstFile = new MockMultipartFile("data", "filename.txt", "text/plain", "some xml".getBytes());
MockMultipartFile secondFile = new MockMultipartFile("data", "other-file-name.data", "text/plain", "some other type".getBytes());
MockMultipartFile jsonFile = new MockMultipartFile("json", "", "application/json", "{\"json\": \"someValue\"}".getBytes());

MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
mockMvc.perform(MockMvcRequestBuilders.multipart("/upload")
.file(firstFile)
.file(secondFile)
.file(jsonFile)
.param("some-random", "4"))
.andExpect(status().is(200))
.andExpect(content().string("success"));
}
}

And the @Configuration class

@Configuration
@ComponentScan({ "test.controllers" })
@EnableWebMvc
public class WebConfig extends WebMvcConfigurationSupport {
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
return multipartResolver;
}
}

The test should pass and give you output of

4 // from param
someValue // from json file
filename.txt // from first file
other-file-name.data // from second file

The thing to note is that you are sending the JSON just like any other multipart file, except with a different content type.

Decoding body parameters with Spring

You receive a string that contains a JSON content. You don't receive a JSON input as application/x-www-form-urlencoded is used as content type and not application/json as stated :

Your Action URL will receive a HTTP POST request, including a payload
body parameter, itself containing an application/x-www-form-urlencoded
JSON string.

So change the parameter type to String and use Jackson or any JSON library to map the String to your Action class :

@RequestMapping(method = RequestMethod.POST, value = "/actions", headers = {"content-type=application/x-www-form-urlencoded"})
public ResponseEntity action(@RequestParam("payload") String actionJSON) {
Action action = objectMapper.readValue(actionJSON, Action.class); 
return ResponseEntity.status(HttpStatus.OK).build();
}

As pvpkiran suggests, you could have replaced @RequestParam by @RequestBody if you could pass the JSON string directly in the body of the POST request, and not as a value of a parameter but it seems that is not the case there.

Indeed by using @RequestBody, the body of the request is passed through an HttpMessageConverter to resolve the method argument.

To answer to your comment, Spring MVC doesn't provide a very simple way to achieve your requirement : mapping the String JSON to your Action class.

But if you really need to automatize this conversion you have a lengthy alternative as stated in the Spring MVC documentation such as Formatters (emphasis is mine) :

Some annotated controller method arguments that represent String-based
request input — e.g. @RequestParam, @RequestHeader, @PathVariable,
@MatrixVariable, and @CookieValue, may require type conversion if the
argument is declared as something other than String.

For such cases type conversion is automatically applied based on the
configured converters. By default simple types such as int, long,
Date, etc. are supported. Type conversion can be customized through a
WebDataBinder, see DataBinder, or by registering Formatters with the
FormattingConversionService, see Spring Field Formatting.

By creating a formatter (FormatterRegistry subclass) for your Action class you could add that in the Spring web config as documented :

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

@Override
public void addFormatters(FormatterRegistry registry) {
// ... add action formatter here
}
}

and use it in your parameter declaration :

public ResponseEntity action(@RequestParam("payload") @Action Action actionJ) 
{...}


Related Topics



Leave a reply



Submit