As title, I use Jersey to return an object as JSON, but the object is created by cglib proxy:
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("test")
public Response test() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(A.class);
enhancer.setCallback(new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return "my name";
}
});
return Response.ok(enhancer.create()).build();
}
#Data
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class A {
private String name;
}
It cannot work because enhancer.create() return a proxy object of class A, not a real object of class A.
org.codehaus.jackson.map.JsonMappingException: No serializer found for
class MyREST$1 and no properties discovered to create BeanSerializer
(to avoid exception, disable
SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference
chain: MyREST$A$$EnhancerByCGLIB$$fdcf8406["callbacks"])
The problem you encounter is that cglib creates a subclass of your class but does not copy the annotations. At the same time, annotations are not inherited if that is not explicitly defined.
Cglib does not support annotations. To overcome this, you can choose to use another code generation library that supports annotations. I wrote such a library, it is called Byte Buddy.
Related
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?
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")
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 ParameterDto2are 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{
...
}
I'm having an issue with building REST architecture for some legacy code.
Jackson ObjectMapper is unable to map my custom object to legacy object, because of 'enums' that really are classes with static final fields.
I tried implementing custom converters/deserializers with no success
In the legacy system there are enums that look like this:
public final class LegacyEnum extends LegacyEnumSuperclass {
public static final LegacyEnum VALUE = new LegacyEnum("1");
I'm receiving values of these 'enums' as Strings, that I convert to legacy enum values (custom deserializer) and set them in my custom class (I need it because I'm using jackson annotations, and I have no access or permission to modify legacy code) and this part works nicely. When I try to map my custom object to legacy object with
objectMapper.convertValue(myCustomObject, LegacyObjectContainingEnums.class);
I get an exception:
Can not construct instance of LegacyEnum: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
The LegacyEnum class has a private constructor, and LegacyEnumSuperclass has a similar protected constructor so I cannot access them (neither can ObjectMapper). I have tried implementing a custom converter, that would skip the 'create new object' part of ObjectMapper mapping, and I also tried to reuse my custom deserializer. I ran into multiple issues and achieved no success.
The most annoying part is, that when I use ModelMapper library it works like a charm (it probably just sets a value in the legacy object, no need to create new LegacyEnum instance like ObjectMapper!) but I'm trying to resolve that issue without adding new dependencies.
Any ideas?
I resolved the issue by using MixIn and custom deserializer, like this:
public abstract class LegacyClassMixIn
#JsonDeserialize(using = LegacyEnumDeserializer.class)
abstract LegacyEnum getLegacyEnum();
Deserializer:
public class LegacyEnumDeserializer extends JsonDeserializer<LegacyEnumSuperclass> implements ContextualDeserializer {
private JavaType valueType;
#Override
public JsonDeserializer createContextual(DeserializationContext context, BeanProperty property) {
JavaType wrapperType = property.getType();
LegacyEnumDeserializer deserializer = new LegacyEnumDeserializer();
deserializer.valueType = wrapperType;
return deserializer;
}
#Override
public LegacyEnumSuperclass deserialize(JsonParser parser, DeserializationContext context) throws IOException {
return LegacyEnumSuperclass.getEnum(valueType.getRawClass(), parser.readValueAs(String.class));
}
valueType.getRawClass() returns LegacyEnum.class, that way I can use one deserializer for all of the 'enums' that inherit LegacyEnumSuperclass class. getEnum is a custom method, from the legacy code.
Registering MixIn in ObjectMapper Spring configuration:
#Configuration
public class ObjectMapperConfig {
public ObjectMapperConfig(ObjectMapper objectMapper) {
objectMapper.addMixIn(LegacyClass.class, LegacyClassMixIn.class);
}
}
That way I can use LegacyClass as a parameter in a Controller method.
Thanks for clues.
I have a set of abstract classes like this:
abstract class A {
public abstract B getB() {return this.b;}
public abstract void setB(B b) {this.b = b;}
}
abstract class B {
public abstract C getC() {return this.c;}
public abstract void setC(C c) {this.c = c;}
}
abstract class C {
private String foo;
public String getFoo() {return this.foo;}
public void setFoo(String foo) {this.foo = foo;}
}
In runtime, I create proxies for these classes using ByteBuddy. I can easily serialize objects of these proxy classes to XML. But when I attempt to deserialize an XML, JAXB throws javax.xml.bind.UnmarshalException: Unable to create an instance of A since it can't create instances of abstract classes. I want to show it how to create these instances in runtime in order to deserialize them (I have a special Spring bean, which does it - so I need to be able to inject it wherever I define creation logic) I looked at JAXB and Jackson, but couldn't find how to do it.
Is there a way to do it? I'm not bound to any serialization framework, though it would be preferable to stay with JAXB or Jackson.
I found that both JAXB and Jackson can do it.
JAXB provides two ways to solve it: factory methods and adapters.
Using JAXB factory methods, I need to create a factory which would be responsible for object creation:
public class MyFactory {
public static MyObject createMyObject() {
return SomeMagic.createMyObject();
}
}
Then I only need to mark my abstract class with #XmlType annotation:
#XmlType(factoryClass = MyFactory.class, factoryMethod = "createMyObject")
public abstract class MyObject {
...
}
If I wanted to use JAXB adapters, I would need to create Java classes which JAXB can instantiate and fill from the XML, and then I would need to convert objects of these classes to the ones I need:
public class MyAdapter extends XmlAdapter<MyAdapter.MyJaxbObject, MyObject> {
#Override
public MyObject unmarshal(MyJaxbObject src) throws Exception {
MyObject tgt = SomeMagic.createMyObject();
BeanUtils.copyProperties(tgt, src);
return tgt;
}
#Override
public MyObject marshal(MyObject src) throws Exception {
MyJaxbObject tgt = new MyJaxbObject();
BeanUtils.copyProperties(tgt, src);
return tgt;
}
public static class MyJaxbObject {
...
}
}
Then I would mark my abstract class with #XmlJavaAdapter annotation:
#XmlJavaAdapter(MyAdapter.class)
public abstract class MyObject {
...
}
Using Jackson I can create custom deserializer for my abstract class.
public class MyObjectDeserializer extends StdDeserializer<MyObject> {
public MyObjectDeserializer() {
super(MyObject.class);
}
#Override
public MyObject deserialize(JsonParser parser, DeserializationContext context) throws IOException {
ObjectMapper mapper = (ObjectMapper) parser.getCodec();
MyObject myObject = SomeMagic.createMyObject();
return mapper.readerForUpdating(myObject).readValue(parser);
}
}
Later in my code I need to register my deserializers:
ObjectMapper mapper = new XmlMapper();
SimpleModule module = new SimpleModule("module", new Version(1, 0, 0, null, null, null));
module.addDeserializer(MyObject.class, new MyObjectDeserializer());
mapper.registerModule(module);
For my purposes I preferred Jackson custom deserializers, because:
I also need to perform additional operations on the nested objects after their fields are filled but before passing these objects to other objects' setters (JAXB doesn't seem to support it)
I can use custom logic when I fill object's fields (Also achievable with Adapters).
I can create deserializers by myself, so I can use dependency injection to configure them (Factories are static, and Adapters are created by JAXB).
We use Google Guice for DI (mostly with constructor injection) and Jackson for object serialization to/from JSON. As such we build our object graph through Guice Modules.
How do we provide/instruct Jackson to use our pre-built Guice Injector? Or it's own injector based on a Guice Module we provide? My preference is to provide it the injector because we already have means to control which module is used based on the environment/configuration we want to run in.
Here's a unit test:
public class Building {
#JsonIgnore
public final ElectricalProvider electricalProvider;
public String name;
#Inject
Building(ElectricalProvider electricalProvider){
this.electricalProvider = electricalProvider;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
public interface ElectricalProvider {}
public class SolarElectricalProvider implements ElectricalProvider{}
#Test
public void testJacksonGuice() throws IOException {
Injector injector = Guice.createInjector(new Module() {
#Override public void configure(Binder binder) {
binder.bind(ElectricalProvider.class).to(SolarElectricalProvider.class);
}
});
Building building1 = injector.getInstance(Building.class);
building1.setName("test building");
ObjectMapper objectMapper = new ObjectMapper();
byte[] buildingJsonBytes = objectMapper.writeValueAsBytes(building1);
Building building2 = objectMapper.readValue(buildingJsonBytes, Building.class);
assertThat(building1, is(equalTo(building2)));
assertThat(building2.electricalProvider, is(instanceOf(SolarElectricalProvider.class)));
}
That when run generates this exception com.fasterxml.jackson.databind.JsonMappingException, with this message: No suitable constructor found for type [simple type, class Building]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
After a bit of googling, I came across the jackson-module-guice project but it doesn't appear to be what we need or doesn't provide as to how to accomplish what we need to do.
Well, i think your approach is anti-pattern. You should serialize and deserialize only POJO object without any business logic and any further dependencies like services, repositories and so on. Could you show your Building class? What's in the constructor?
You should split your Building class into Service and Domain layers. The Service layer would use Domain and there you should inject all dependencies. The Domain layer would be only POJO bean with default constructor, no final fields and only getter/setter methods.
If you need to use final properties and constructor initialization you have few options as mentioned in Jackson 3rd Party Class With No Default Constructor
I am using JAX-RS 2.0 with Jersey 2.6. I was wondering if it was possible to have something like this:
#GET
#Path("/get/{id}")
#MapTo(type = MyObjectDTO.class)
public MyObject getMyObject(#PathParam("id") String id){
MyObject o = ...
return o;
}
In the method above I am returning an instance of MyObject. However, I have defined the MapTo annotation to indicate that I want to map this object to MyObjectDTO. The way I was thinking this could work is to process the response early in a ContainerResponseFilter, detect the annotation MapTo and, assuming no error occurred, replace the entity in the response with an instance of MyObjectDTO created appropriately from the existing entity (of type MyObject).
However, I couldn't find a way to get the Method in the resource that was just called after the request came in, i.e., the getMyObject method, so that I can scan for the MapTo annotation.
Is there a way to achieve this in a JAX-RS-y kind of way?
Is this some serious reason you cannot return dto object? Sounds very strange...You can probably use AOP but I guess it would be bad practive
Here the Spring AOP example
http://docs.spring.io/spring/docs/2.5.4/reference/aop.html
I think I found a solution by reading this SO. I created a class that looks like this:
#Provider // or register in the configuration...
public class DTOMapperFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
for (Annotation annotation : resourceInfo.getResourceMethod().getAnnotations()) {
if (annotation instanceof MapTo) {
MapTo mapTo = (MapTo) annotation;
// Note: additional validation (return type shouldn't be void,
// collections are out etc.) is required before creating this,
// or should be pushed in the DTOMapperFilter.
// You get the gist: this filter will map the entity to an instance
// of the specified class (using a constructor in this case).
context.register(new DTOMapperFilter(
resourceInfo.getResourceMethod().getReturnType(),
mapTo.getResponseType());
}
}
}
#Priority(/* appropriate priority here! */)
public final static class DTOMapperFilter implements ContainerResponseFilter {
public DTOMapperFilter(Class<?> declaredReturnType, Class<?> responseType) {
// implementation omitted: find DTO constructor etc.
// throw if responseType does NOT have a constructor that takes an instance
// of declaredReturnType: catch errors at application bootstrap!
}
#Override
public void filter(
ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
// implementation omitted: create instance of DTO class using constructor
}
}
}
Given sensible exceptions will be thrown from either the constructor of DTOMapperFilter or the configure method above, this should be pretty robust and errors detectable at test time.