2

I have an application that uses Spring Boot to provide REST capability. I'm running into a problem deserializing a POST response into a POJO. The exception is as follows:

org.springframework.http.converter.HttpMessageConversionException: Type definition error: [collection type; class uci.BoundedList, contains [simple type, class java.lang.Object]]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `uci.BoundedList` (no Creators, like default construct, exist): no default no-arguments constructor found

The BoundedList type is part of an API that is generated from an XML schema using XJC. I have no control over how this class is generated. It turns out that it's a subclass of java.util.ArrayList and has only one constructor defined:

public BoundedList(int minOccurs, int maxOccurs) {
    super();
    this.minOccurs = minOccurs;
    this.maxOccurs = maxOccurs;
}

It does not define a no argument constructor, which is what the exception seems to be complaining about.

Since I cannot modify this class and it is an integral part of the API I'm using, what can I do to get around this problem? Can I supply some sort of customized class/interface that will satisfy the Jackson data binding? Some other possible solution?

UPDATE:

I have tried a number of suggestions based upon answers provided below. None of them work. I'm investigating the "mix-in" approach and I'll admit I don't quite understand how it's supposed to work. The many articles I've read are written simply, but it still seems to be a little "black magic".

In any case, below are snippets of what I've tried to do based on what I've read:

The "mix-in" class:

public abstract class BoundedListMixin {
  @JsonCreator
  public BoundedListMixin(@JsonProperty("minOccurs") int minOccurs,
      @JsonProperty("maxOccurs") int maxOccurs) {}
}

What was added to an existing configuration class:

@Configuration
@EnableAsync
@EnableScheduling
@ComponentScan("<package>")
public class ServiceConfigurer {
  @Bean
  @Primary
  public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    ObjectMapper mapper = builder.createXmlMapper(false).build();
    mapper.addMixIn(BoundedList.class, BoundedListMixin.class);
    return mapper;
  }
}

I can confirm that the objectMapper() method in the configuration class is being called via the debugger.

This link (https://dzone.com/articles/jackson-mixin-to-the-rescue) introduces another facet, but alas, that does not work either.

UPDATE:

I have replaced the objectMapper() method in ServiceConfigurer (above) with the following:

  @Bean
  public Jackson2ObjectMapperBuilderCustomizer objectMapperCustomizer() {
    return new Jackson2ObjectMapperBuilderCustomizer() {
      @Override
      public void customize(Jackson2ObjectMapperBuilder builder) {
        builder.mixIn(BoundedList.class, BoundedListMixin.class);
      }
    };
  }

I STILL get the same problem. The problem has to be related to some other issue.

NOTE:

I should also clarify that this issue manifests when I made a GET call. I have a simple REST endpoint that just requests a service return a POJO. Oddly, I if send the request from a browser, the object is returned as JSON and rendered. But, when it's called from the code, I get the exception above.

The GET call:

RequestStatus statusMsg = template.getForObject("http://localhost:8080/rst/missionPlanning/data", RequestStatus.class);
Joseph Gagnon
  • 1,731
  • 3
  • 30
  • 63

3 Answers3

2

Jackson needs a default constructor without parameters or annotations on the constructor paramterts to find out which field in the JSON object should be mapped to the parameter. Something like this:

@JsonCreator
public BoundedList(@JsonProperty("min") int minOccurs, @JsonProperty("max") int maxOccurs) {
   // your code
}

When you can't change the class, then you can use Jackson MixIns

abstract class MixIn {
   MixIn(@JsonProperty("min") int minOccurs, @JsonProperty("max") int maxOccurs) { }
}

and configure it on the object mapper like so:

objectMapper.addMixInAnnotations(BoundedList.class, MixIn.class);

See https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations for details.

spa
  • 5,059
  • 1
  • 35
  • 59
  • The "MixIn" approach you've suggested (also suggested by @Mike below) sounds very interesting and seems like it might solve my problem. What I'm not sure about though, is that it seems to be something that is registered with the Jackson `ObjectMapper`. I'm not explicitly using ObjectMapper in my situation. I am using a `RestTemplate` to make a `POST` request and the object being returned is where the problem is occurring (at least that's what I think). How could I (can I?) involve ObjectMapper (and the mix-in) into this? – Joseph Gagnon Jul 25 '19 at 15:15
  • Here is the call: `ResponseEntity response = template.postForEntity("http://localhost:8080/rst/missionPlanning/generateRoute", entity, CommandStatus.class);` – Joseph Gagnon Jul 25 '19 at 15:18
  • You can find an example here: https://gdpotter.com/2017/05/24/custom-spring-mvc-jackson/ (probably you find many more googling). The idea is that you register your own ObjectMapper bean. Spring Boot helps you with that. – spa Jul 25 '19 at 15:20
  • I tried doing as suggested (actually a couple of different ways based upon what I found by googling) and none of them seem to work. I still get the same exception. :( – Joseph Gagnon Jul 25 '19 at 16:44
  • Here is one link that seems very apropos for my situation. However it still does not work. http://blogs.jbisht.com/blogs/2016/09/12/Deserialize-json-with-Java-parameterized-constructor – Joseph Gagnon Jul 25 '19 at 17:16
  • @JosephGagnon Spring boot provides a default object mapper. It can be customized using various app configs, and you can also create a bean of type `Jackson2ObjectMapperBuilderCustomizer` in your app to apply any mix-ins and other customizations. – Mike Jul 25 '19 at 18:01
  • @JosephGagnon This answer has a decent example: https://stackoverflow.com/a/48519868/657224 ... you would need to called `jacksonObjectMapperBuilder.mixIn(...)` to set your custom mix in class for BoundedList. – Mike Jul 25 '19 at 18:21
1

The easiest way is to add Jackson annotations to the POJO to instruct it how to deserialize. This question describes it pretty well.

However, since your POJO is auto generated and you cannot modify it, there are 2 other options:

  1. Use a custom deserializer to manually parse the JSON payload into an instance of your POJO
  2. Use a mix-in to decorate the POJO with the desired annotations
Mike
  • 4,722
  • 1
  • 27
  • 40
0

You could map it to a different type, then convert it to a bounded list.

Artanis
  • 561
  • 1
  • 7
  • 26