I have two java endpoints in spring boot like this:
@PostMapping(path="/my-import-1")
@ResponseStatus(HttpStatus.OK)
public String myImport1(@Valid @RequestBody ParameterDto1 params) {
return this.serviceImpl.import(params);
}
and
@PostMapping(path="/my-import-2")
@ResponseStatus(HttpStatus.OK)
public String myImport2(@Valid @RequestBody ParameterDto2 params) {
return this.serviceImpl.import(params);
}
Both use the same service for importing, but have some differences in their parameters.
I created the service's import method like this
@Override
public String import(ParameterInterface params) throws Exception {
...
}
and the ParameterInterface like this
public interface ImportMetaData {
public default ArrayList<FileInterface> getFiles() {
return null;
}
public void setFiles(ArrayList<FileInterface> files);
}
Implementing this interface I created two ParameterDto classes (ParameterDto1 and ParameterDto2). The IDE shows everything is correct, also the start of my service works, but as soon as I send a request to one of the endpoints, I get the following error:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.beo.services.myImportService.rest.domain.dto.metadata.interfaces.ParameterInerface]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
com.beo.services.myImportService.rest.domain.dto.metadata.interfaces.ParameterInerface
(no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information at [Source: (PushbackInputStream); line: 3, column: 5] (through reference chain: com.beo.services.myImportService.rest.domain.dto.metadata.ParameterDto["files"]->java.util.ArrayList[0])] with root cause
Can I any how create such an ArrayList from an interface and get these two endpoints running? Or is there another solution?
CodePudding user response:
The issue is with the ParameterDto1 and ParameterDto2. Jackson library requires a default, no-args constructor or a constructor with parameters annotated with @JsonProperty("field_name")
, otherwise it cannot convert your message.
Solution:
Add a no-args constructor to ParameterDto1 and ParameterDto2 or annotate the constructor parameters with @JsonProperty("field_name")
CodePudding user response:
im guessing here because you didnt share the implementation of ParameterDto1
or ParameterDto2
- and for some reason your interface is called ImportMetaData
where according to the exception, your explanation and other files it should be ParameterInterface
.
the problem is that getFiles/setFiles is considered as a property by jackson , its type is an interface and you are not sending any type information.
in general assuming ParameterDto1
and ParameterDto2
are using a concreate implementation of FileInterface
you could just change your interface methods getFiles
/setFiles
so they are using generics parameter and in each implementation set the concreate type for FileInterface
you are using , this will allow jackson to understand the concreate type for FileInterface
.
incase ParameterDto1
and ParameterDto2
are not using a concreate implementation of FileInterface
you should add @JsonTypeInfo
or @JsonSubTypes
(see https://www.baeldung.com/jackson-annotations section 5 for more info) - note that the client calling the api should also specify the actual type in the json-type field
Suggested implementation
public interface ParameterInterface {
@JsonIgnore
public List<FileInterface> getParameters() default { return null;}
.....
}
public class ParameterDto1 implements ParameterInterface {
private List<FileImpl1> files;
public List<FileImpl1> getFiles(){return files;}
public void setFiles(List<FileImpl1> files){this.files=files;}
....
}
public class ParameterDto2 implements ParameterInterface {
private List<FileImpl2> files;
public List<FileImpl2> getFiles(){return files;}
public void setFiles(List<FileImpl2> files){this.files=files;}
...
}
public class FileImpl1 implements FileInterface{
...
}
public class FileImpl2 implements FileInterface{
...
}