Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

We were using FasterXML on the same object to generate JSON and XML. Unfortunately, the object itself didn’t define the desired markup. It used JAXB annotations to add a wrapper around a set of data.

XML and JSON output based upon class definition

The class had the following property defined:

public List<TestRacingEventResponse> getRacingEvents() { return _racingEvents; }
public void setRacingEvents(List<TestRacingEventResponse> racingEvents) { this._racingEvents = racingEvents; }

This would generate XML like:

<racingEvents>
    <....>
</racingEvents>
<racingEvents>
    <....>
</racingEvents>
<racingEvents>
    <....>
</racingEvents>

The JSON would be outputted like this:

{racingEvents
{…}
}
{racingEvents
{…}
}
{racingEvents
{…}
}

JAXB to create a wrapper and redefine the element

This isn’t what was wanted. The desired output was a racingEvents container around a list of racingEvent containers. This is quite easy in XML via JAXB. The declaration with the required JAXB is as follows:

@XmlElementWrapper(name="racingEvents")
@XmlElement(name="racingEvent")
public List<TestRacingEventResponse> getRacingEvents() { return _racingEvents; }
public void setRacingEvents(List<TestRacingEventResponse> racingEvents) { this._racingEvents = racingEvents; }

This would generate XML like:

<racingEvents>
    <racingEvent>
        <....>
    </racingEvent
    <racingEvent>
        <....>
    </racingEvent
    <racingEvent>
        <....>
    </racingEvent
</racingEvents>

This works great for XML, but JSON doesn’t honor JAXB annotations. This leaves us with two alternatives. Create a class structure that mimics the structures you wanted. That would be creating a RacingEvents property in our enclosing class that is a List, then create a RacingEvent object that has the required fields.

Configure FasterXML (Jackson 2) to honor JAXB Wrapper annotation

Another way to do this is to instruct the JSON marshaller to honor the JAXB annotations. We are using Jackson 2 (FasterXML). In our particular case, we were using CXF for the front-end services. The original XML configuration is as follows:

<jaxrs:server id="hptsWebService" address="/">
    <jaxrs:serviceBeans>
        <bean id="accountServices" class="com.twinspires.hpts.servicedefinitions.AccountServices"/>
        <bean id="racingContentServices" class="com.twinspires.hpts.servicedefinitions.RacingContentServices"/>
        <bean id="systemInfoServices" class="com.twinspires.hpts.servicedefinitions.SystemInfoServices"/>
        <bean id="testerServices" class="com.twinspires.hpts.servicedefinitions.TesterServices"/>
    </jaxrs:serviceBeans>
    <jaxrs:providers>
        <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/>
    </jaxrs:providers>
</jaxrs:server>

I needed to be able to configure the ObjectMapper. Unfortunately, I couldn’t get to the ObjectMapper in the code because the JacksonJaxbJsonProvider only exposed a MAPPER. That seems like a bug to me. To get around this, I declared the JacksonJaxbJsonProvider separately.

<bean id="jacksonJaxbJsonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/>

<jaxrs:server id="hptsWebService" address="/">
    <jaxrs:serviceBeans>
        <bean id="accountServices" class="com.twinspires.hpts.servicedefinitions.AccountServices"/>
        <bean id="racingContentServices" class="com.twinspires.hpts.servicedefinitions.RacingContentServices"/>
        <bean id="systemInfoServices" class="com.twinspires.hpts.servicedefinitions.SystemInfoServices"/>
        <bean id="testerServices" class="com.twinspires.hpts.servicedefinitions.TesterServices"/>
    </jaxrs:serviceBeans>
    <jaxrs:providers>
        <ref bean="jacksonJaxbJsonProvider"/>
    </jaxrs:providers>
</jaxrs:server>

Now that the object is separate, I had to find a place where I could manipulate it. I found an object that was related to the functionality that I needed and added a constructor that took the JacksonJaxbJsonProvider as input. Once I had that object, I could configure to my heart’s content.

My modifications involved creating a new OjbectMapper. I preferred to get the ObjectMapper that was being used, but there was no getter. Once I have the ObjectMapper, I set a property that indicates that I want use the XmlElementWrapper as a wrapper for my output.

    @Autowired
    public HttpUtil(JacksonJaxbJsonProvider jacksonJaxbJsonProvider) {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enable(MapperFeature.-- USE_WRAPPER_NAME_AS_PROPERTY_NAME);
        jacksonJaxbJsonProvider.setMapper(objectMapper);
    }

Now that we have told the JSON marshaller to honor the JAXB wrapper annotation, output should be:

{racingEvents
    {races
        {...}
    }
    {races
        {...}
    }
    {races
        {...}
    }
}

Conclusion

To have a FasterXML honor JAXB annotations when it outputs JSON, you have to instantiate the JacksonJaxbJsonProvider separately so it can be injected. Once it is injected in code, you can create and configure an ObjectMapper.

June 10th, 2016

Posted In: FasterXML, Jackson 2, Java, java ninja, Javaninja, JAXB, json

One Comment

Most of the marshalling I have to do it done by Spring. However, I sometimes have to do this manually. Let’s see how easy it is with Jackson 2 (FasterXML).

Maven dependency

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.4.3</version>
</dependency>

Import the ObjectMapper

The ObjectMapper is the object that you will instantiate and use for all conversions.

import com.fasterxml.jackson.databind.ObjectMapper;

Object to String

Given an object, it is pretty easy to use an ObjectMapper.

ObjectMapper objectMapper = new ObjectMapper();
String myString = objectMapper.writeValueAsString(myObject);

String to Object

If you have a JSON string and need to convert that to an object, you also use the ObjectMapper.

ObjectMapper objectMapper = new ObjectMapper();
MyObject myObject = objectMapper.readValue(myJsonString, MyObject.class);

May 12th, 2016

Posted In: FasterXML, Java, java ninja, Javaninja, json

Leave a Comment

LinkedIn Auto Publish Powered By : XYZScripts.com