I am running a payara micro server and having some difficulties serializing an object.
The root object has a child object which is either of type A
or B extends A
. The issue is that it always serialized into json as if it was of type A
, i.e. no properties that are in the type B
are being serialized.
I have checked that the object is correct right after returning the Response
object.
The pom.xml
looks like this:
<build>
<finalName>profile</finalName>
<plugins>
<plugin>
<groupId>fish.payara.maven.plugins</groupId>
<artifactId>payara-micro-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<goals>
<goal>bundle</goal>
<goal>start</goal>
</goals>
</execution>
</executions>
<configuration>
<payaraVersion>4.1.2.173</payaraVersion>
<useUberJar>true</useUberJar>
<deployWar>false</deployWar>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile-bom</artifactId>
<version>1.1.0</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.19.4</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
<version>1.5.20</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.1.Final</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1212</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
</dependencies>
And I was thinking that maybe I should just change to org.glassfish.jersey.media.jersey-media-json-jackson
and maybe that would help, but then I just get this exception HK2 failure has been detected in a code that does not run in an active Jersey Error scope.
So I am not really sure what to do at this point. Is there any way to config the com.sun.jersey.jersey-json
parser, or should I just give up on that and try to force jersey-media-json-jackson
in there instead?
edit: I've realized that payara uses MOXy json by default, but the issue is still the same
To show some examples, I have this somewhat shortened code:
public class CategoryDTO {
private String name;
private List<QuestionDTO> questions;
}
public class QuestionDTO {
private String id;
}
public class QuestionAdminDTO extends QuestionDTO {
private String comment;
}
and when it is serialized I get
{
"name":"some name",
"questions":[
{
"id":"some id"
}
]
}
but I am expecting that comment
should be included like this
{
"name":"some name",
"questions":[
{
"id":"some id",
"comment":"some comment"
}
]
}
After testing some more it works if I try to marshal a QuestionDTO object straight away, but not if it is nestled
My application is configured with a /webapp/WEB-INF/glassfish-web.xml
file that looks like this
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD
GlassFish Application Server 3.1 Servlet 3.0//EN"
"http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app>
<session-config>
<session-manager/>
</session-config>
<class-loader delegate="false"/>
</glassfish-web-app>
As well as a "custom" Application
class:
@ApplicationPath("/api")
public class RestApplication extends Application {
public RestApplication() {
Swagger swagger = new Swagger();
swagger.securityDefinition("JWT_AUTHORIZATION", new ApiKeyAuthDefinition("access_token", In.HEADER));
new SwaggerContextService().updateSwagger(swagger);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setDescription("profile");
beanConfig.setTitle("Profile");
beanConfig.setVersion("1.0-SNAPSHOT");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setBasePath("/profile/api");
beanConfig.setResourcePackage("se.profile");
beanConfig.setScan(true);
}
}
the stacktrace I get when adding jackson
[2018-06-21T10:37:26.526+0200] [] [WARNING] [] [org.glassfish.jersey.internal.Errors] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1529570246526] [levelValue: 900] [[
The following warnings have been detected: WARNING: HK2 failure has been detected in a code that does not run in an active Jersey Error scope.
WARNING: Unknown HK2 failure detected:
MultiException stack 1 of 1
javax.enterprise.inject.CreationException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at org.jboss.weld.security.NewInstanceAction.run(NewInstanceAction.java:33)
at java.security.AccessController.doPrivileged(Native Method)
at org.jboss.weld.injection.Exceptions.rethrowException(Exceptions.java:40)
at org.jboss.weld.injection.Exceptions.rethrowException(Exceptions.java:50)
at org.jboss.weld.injection.Exceptions.rethrowException(Exceptions.java:90)
at org.jboss.weld.injection.ConstructorInjectionPoint.newInstance(ConstructorInjectionPoint.java:127)
at org.jboss.weld.injection.ConstructorInjectionPoint.invokeAroundConstructCallbacks(ConstructorInjectionPoint.java:92)
at org.jboss.weld.injection.ConstructorInjectionPoint.newInstance(ConstructorInjectionPoint.java:78)
at org.jboss.weld.injection.producer.AbstractInstantiator.newInstance(AbstractInstantiator.java:28)
at org.jboss.weld.injection.producer.BasicInjectionTarget.produce(BasicInjectionTarget.java:112)
at org.jboss.weld.injection.producer.BeanInjectionTarget.produce(BeanInjectionTarget.java:180)
at org.glassfish.jersey.ext.cdi1x.internal.AbstractCdiBeanHk2Factory$2.getInstance(AbstractCdiBeanHk2Factory.java:139)
at org.glassfish.jersey.ext.cdi1x.internal.AbstractCdiBeanHk2Factory._provide(AbstractCdiBeanHk2Factory.java:91)
at org.glassfish.jersey.ext.cdi1x.internal.GenericCdiBeanHk2Factory.provide(GenericCdiBeanHk2Factory.java:63)
at org.jvnet.hk2.internal.FactoryCreator.create(FactoryCreator.java:153)
at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:487)
at org.jvnet.hk2.internal.PerLookupContext.findOrCreate(PerLookupContext.java:70)
at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2126)
at org.jvnet.hk2.internal.ServiceLocatorImpl.internalGetService(ServiceLocatorImpl.java:777)
at org.jvnet.hk2.internal.ServiceLocatorImpl.internalGetService(ServiceLocatorImpl.java:740)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:710)
at org.glassfish.jersey.server.ApplicationHandler.createApplication(ApplicationHandler.java:385)
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:342)
at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:392)
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:177)
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:369)
at javax.servlet.GenericServlet.init(GenericServlet.java:244)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1495)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1294)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5303)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:5548)
at com.sun.enterprise.web.WebModule.start(WebModule.java:522)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:917)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:900)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:684)
at com.sun.enterprise.web.WebContainer.loadWebModule(WebContainer.java:2057)
at com.sun.enterprise.web.WebContainer.loadWebModule(WebContainer.java:1703)
at com.sun.enterprise.web.WebApplication.start(WebApplication.java:107)
at org.glassfish.internal.data.EngineRef.start(EngineRef.java:122)
at org.glassfish.internal.data.ModuleInfo.start(ModuleInfo.java:294)
at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:357)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:501)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:220)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:488)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:544)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:540)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:360)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2.execute(CommandRunnerImpl.java:539)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$3.run(CommandRunnerImpl.java:570)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$3.run(CommandRunnerImpl.java:562)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:360)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:561)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1469)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1300(CommandRunnerImpl.java:111)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1851)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1727)
at com.sun.enterprise.admin.cli.embeddable.DeployerImpl.deploy(DeployerImpl.java:134)
at com.sun.enterprise.admin.cli.embeddable.DeployerImpl.deploy(DeployerImpl.java:149)
at fish.payara.micro.impl.PayaraMicroImpl.deployAll(PayaraMicroImpl.java:1377)
at fish.payara.micro.impl.PayaraMicroImpl.bootStrap(PayaraMicroImpl.java:993)
at fish.payara.micro.impl.PayaraMicroImpl.main(PayaraMicroImpl.java:186)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at fish.payara.micro.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
at fish.payara.micro.boot.loader.Launcher.launch(Launcher.java:107)
at fish.payara.micro.boot.loader.Launcher.launch(Launcher.java:70)
at fish.payara.micro.boot.PayaraMicroLauncher.main(PayaraMicroLauncher.java:79)
at fish.payara.micro.PayaraMicro.main(PayaraMicro.java:361)
Caused by: java.lang.NoClassDefFoundError: com/fasterxml/jackson/annotation/JsonMerge
at com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector.<clinit>(JacksonAnnotationIntrospector.java:50)
at com.fasterxml.jackson.databind.ObjectMapper.<clinit>(ObjectMapper.java:291)
at io.swagger.util.ObjectMapperFactory.create(ObjectMapperFactory.java:35)
at io.swagger.util.ObjectMapperFactory.createJson(ObjectMapperFactory.java:23)
at io.swagger.util.ObjectMapperFactory.createJson(ObjectMapperFactory.java:19)
at io.swagger.util.Json.mapper(Json.java:13)
at io.swagger.jaxrs.DefaultParameterExtension.<init>(DefaultParameterExtension.java:50)
at io.swagger.jaxrs.ext.SwaggerExtensions.<clinit>(SwaggerExtensions.java:36)
at io.swagger.jaxrs.utils.ReaderUtils.collectParameters(ReaderUtils.java:123)
at io.swagger.jaxrs.utils.ReaderUtils.collectFieldParameters(ReaderUtils.java:96)
at io.swagger.jaxrs.Reader.read(Reader.java:291)
at io.swagger.jaxrs.Reader.read(Reader.java:175)
at io.swagger.jaxrs.config.BeanConfig.scanAndRead(BeanConfig.java:242)
at io.swagger.jaxrs.config.BeanConfig.setScan(BeanConfig.java:221)
at se.profile.RestApplication.<init>(RestApplication.java:30)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.jboss.weld.injection.ConstructorInjectionPoint.newInstance(ConstructorInjectionPoint.java:119)
... 66 more
Caused by: java.lang.ClassNotFoundException: com.fasterxml.jackson.annotation.JsonMerge
at org.glassfish.web.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1655)
at org.glassfish.web.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1501)
... 86 more
]]
Easiest solution is to use Jackson. MOXy is based off JAXB rules, which can get very complicated in some situations (this includes working with inheritance). Jackson just works.
Just add the Jackson dependency (in a provided scope, as Payara already has the dependency) and register the JacksonFeature
with the application.
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.25.1</version>
<scope>provided</scope>
</dependency>
I can't see how you configuring your application, so I can't advise on the appropriate way to register the JacksonFeature
. If you don't know how to do this, you can either update your post with your application configuration or you can just search for how you register it.
To register the JacksonFeature
in the Application
class, what you can do is use a property
.
public class RestAppication extends Application {
@Override
public Map<String, Object> getProperties() {
Map<String, Object> props = new HashSet<>();
String classnames = JacksonFeature.class.getName();
props.put("jersey.config.server.provider.classnames", classnames);
props.put("jersey.config.disableMoxyJson", true);
return props;
}
}
If you get a Jackson dependency conflict, which might occur with your use of Swagger (which pulls Jackson in), you cam just exclude Jackson from Swagger, as the server should already have the dependencies
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
<version>1.5.20</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<exclusion>
</exclusions>
</dependency>