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

Viewed 9666

From the frontend I receive GET-request which contains encoded json string as one of its parameters:

http://localhost:8080/engine/template/get-templates?context=%7B%22entityType%22%3A%22DOCUMENT%22%2C%22entityId%22%3A%22c7a2a0c6-fd34-4f33-9cb8-14c2090565ea%22%7D&page=1&start=0&limit=25  

Json-parameter 'context' without encoding (UUID is random):

{"entityType":"DOCUMENT","entityId":"c7a2a0c6-fd34-4f33-9cb8-14c2090565ea"}

On backend my controller's method which handle that request looks like this:

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

'Context' domain class:

public class Context {
    private String entityType;
    private UUID entityId;

    public String getEntityType() {
        return entityType;
    }
    public void setEntityType(String entityType) {
        this.entityType = entityType;
    }
    public UUID getEntityId() {
        return entityId;
    }
    public void setEntityId(UUID entityId) {
        this.entityId = entityId;
    }
}

I believed Spring's Jackson module would automatically convert that kind of json to java object of Context class, but when I run this code it gives me exception:

org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type 'com.company.domain.Context'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.company.domain.Context': no matching editors or conversion strategy found

On StackOverflow I've seen similar questions, but those were about POST-requests handling (with @RequestBody annotation), which doesn't fit with GET-request.

Could you help me to solve this problem? Thanks in advance.

3 Answers

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); 
   //...
}

As far as I know Spring does not have the mechanism to convert from a String to a UUID in older releases. In such case you should declare you entityId as a String and then use a converter in order to convert it to UUID.
So your Context class should be like below:

public class Context {
    private String entityType;
    private String entityId;

    public String getEntityType() {
        return entityType;
    }
    public void setEntityType(String entityType) {
        this.entityType = entityType;
    }
    public String getEntityId() {
        return entityId;
    }
    public void setEntityId(String entityId) {
        this.entityId = entityId;
    }
    public UUID getEntityIdAsUUID() {
        return convertToUUID(this.entityId);
    }

    // Helper Conversion String to UUID method
    private UUID convertToUUID(String entityId){
        return UUID.fromString(entityId);
    }

}

I faced the same issue in both Jersey and Spring MVC when trying to convert the JSON String {"x":"1001822.831","y":"200716.8913"} to a object of a class called Point

Point class is as below

public class Point
{
    private Double x;
    private Double y;

//getters and setters
}

As per Jersey documentation, I added the below method to Point class and it worked for both Jersey and Spring MVC.

//used by jax rs & spring mvc for converting queryParam String to Point
    public static Point valueOf(String json) throws IOException
    {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.readValue(json, Point.class);
    }

Please refer to section 3.2 here https://jersey.github.io/documentation/latest/jaxrs-resources.html#d0e2271

Related