I'm using Jersey and Jackson to create a simple JSON API.
Some of the objects being serialized have custom enum fields. By default, those enums are converted to a string based on the enum vale -- I would like the enums to have slightly more complex serializations.
I'm using Jackson annotations within the enum, but the endpoint seems to be ignoring them. I've been spinning my wheels trying to figure out where the issue is, and now I turn to you.
Enum Code
package org.example.code;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonFormat(shape= JsonFormat.Shape.OBJECT)
public enum ExampleEnum {
YES (1, "Yes indeed"),
NO (2, "No way buddy")
private final Integer code;
private final String description;
ExampleEnum(final Integer code, final String description) {
this.code = code;
this.description = description;
}
@JsonProperty("code")
public Integer getCode() {
return code;
}
@JsonProperty("description")
public String getDescription() {
return description;
}
}
API Code
package org.example.webservice.impl;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.example.code.ExampleEnum;
@Path("/example")
public class ExampleService {
@GET
@Path("/test")
@Produces({MediaType.APPLICATION_JSON})
public ExampleEnum getExampleEnum() {
return ExampleEnum.YES;
}
}
When I call the endpoint example/test
the output is YES
What I want is the output to be something along the lines of { code: 1, description: "Yes indeed" }
Configuration files are below...
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>api</artifactId>
<version>0.0.1</version>
<packaging>war</packaging>
<name>example API</name>
<url>http://example.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>2.18</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<url>http://localhost:8080/manager/text</url>
<server>TomcatServer</server>
<path>/example</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name></display-name>
<servlet>
<display-name>Example Servlet</display-name>
<servlet-name>Example Servlet</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>org.example.webservice.impl</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.filter.LoggingFilter;org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Example Servlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
So a Few things:
com.sun.jersey.api.json.POJOMappingFeature
is for Jersey 1.x, so you can get rid of that.
MOXy is the default provider in Glassfish, so if you want to use Jackson, then you need to disable MOXy. You can do so by adding this <init-param>
<init-param>
<param-name>jersey.config.server.disableMoxyJson</param-name>
<param-value>true</param-value>
</init-param>
If you are using a ResourceConfig
for your configuration, you can use the property
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
property(ServerProperties.MOXY_JSON_FEATURE_DISABLE, true);
}
}
The constant value is actually jersey.config.server.disableMoxyJson
.
Then add the Jackson provider
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey2.version}</version>
<scope>provided</scope>
</dependency>
The Jersey version should match whatever version you are using for your project. One very important thing to note though is that if you are using Glassfish, you SHOULD put all your Jersey dependencies in a <scope>provided</scope>
because the server already has the jars in its library. You do not want your project dependencies to conflict with the jars that are already in the server.
Next you need to register the Jackson provider with your app. If you are using web.xml you can add the JacksonFeature
to the list on the ...classnames
property
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
org.glassfish.jersey.filter.LoggingFilter,
org.glassfish.jersey.media.multipart.MultiPartFeature,
org.glassfish.jersey.jackson.JacksonFeature
</param-value>
</init-param>
If you are using a ResourceConfig
for your configuration, then you can just register the JacksonFeature
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(JacksonFeature.class);
}
}
Another thing that is unrelated to this issue that I might point out, is that Glassfish 4 already has a Jersey implementation, which is a very old 2.x version, maybe 2.0. When you add runtime Jersey dependencies, they might conflict. I would do one of two things, either put all the Jersey dependencies in a provided
<scope>
or if you have requirements for later version functionality, you may want to look into updating the Jersey version in Glassfish. Have a look at Updating Jersey 2 in GlassFish 4
Glassfish has jackson jar within itself and it may have conflicts with explicit jackson jars that you have declared in pom.xml .
In maven website, for each version of the jar, there are few other compile time dependencies jars within the jar you have chosen.
So those might also be conflicting with the jars that you have declared in pom.xml
I also removed
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.24</version>
</dependency>
from pom.xml
For Jersey 2.2x users who use Application class rather than using web.xml, this works for me
1. In ResourceConfig,
@ApplicationPath("rest")
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("com.example.rest");
register(JacksonFeature.class);
}
}
Or, if you are using Application class,
public class MyApplication extends Application {
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<Class<?>>();
.
.
classes.add(JacksonFeature.class);
return classes;
}
}
2. In pom.xml, add this dependency,
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.26</version>
</dependency>
3. Make sure you have annotations on getters and setters as well. In my case @JsonIgnore annotation worked only when I used it on getter and setter of that class variable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With