How to: Make the provided ObjectMapper use JAXB Annotations

SDK: 8.0.16
JIRA: 7.13.11

I needed an ObjectMapper to do some talking to a 3rd party REST API. To keep things aligned, I created the entities with JAXB annotations, just like JIRA REST modules use.

I created a JsonUtil class with an ObjectMapper singleton (they are expensive; if you use it often, re-use it; it’s thread safe). Turns out, this doesn’t pick up the JAXB annotations by default, so all my element names (from the @XmlElement(name = "my_custom_name_that_would_suck_to_use") annotations) were not being picked up.

I then researched for a while to see how to grab the instance that JIRA uses. The normal way of using the javax.ws.rs.core.Context annotation didn’t work for me:

    @Context
    private ContextResolver<ObjectMapper> mapperResolver;

Due to OSGi, maybe?

I gave up and decided to just add support to the singleton I created.

Most searches will tell you to add a dependency on com.fasterxml.jackson.module:jackson-module-jaxb-annotations and use modules:

ObjectMapper objectMapper = new ObjectMapper()
        .withModule(new JaxbAnnotationModule());

This module does not exist in the project; so, I conclude that JIRA is not doing it this way. At this point, you could find the right dependency and add the module, but if JIRA is doing it already without the module, why add another dependency? So, we continue in our search.

The other way to add support is by adding the JaxbAnnotationIntrospector. Ah! JIRA does provide this class!

So, here it is:

package com.example.myjiraplugin.utils;

import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;

import java.io.IOException;

public final class JsonUtil {

    private static final ObjectMapper mapper = new ObjectMapper()
            .setAnnotationIntrospector(new JaxbAnnotationIntrospector())
            .setSerializationInclusion(JsonSerialize.Inclusion.NON_EMPTY)
            .enable(DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
            .disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);

    private JsonUtil() throws IllegalAccessException {
        throw new IllegalAccessException("Utility class");
    }

    public static String toJson(Object obj) throws IOException {
        return mapper.writeValueAsString(obj);
    }

    public static <T> T toObject(String string, Class<T> clazz) throws IOException {
        return mapper.readValue(string, clazz);
    }

}

The serialization/deserialization flags are obviously not needed to get the JAB Annotations working - these are simply a combination of what my project required and my personal preference.

WARNING: This will actually disable the Jackson annotations! To use both, check out the README.md here: jackson-modules-base/jaxb at master · FasterXML/jackson-modules-base · GitHub

If you want to look into the provided dependencies, here is an excerpt from atlas-mvn dependency:tree:

[INFO] +- com.atlassian.jira:jira-api:jar:7.13.11:provided
[INFO] |  +- org.codehaus.jackson:jackson-core-asl:jar:1.9.13-atlassian-1:provided
[INFO] |  +- org.codehaus.jackson:jackson-mapper-asl:jar:1.9.13-atlassian-1:provided

Hope this helps someone!

If you know of a way to get a hold of JIRA’s ObjectMapper (or one configured the same way), please post it here!

2 Likes