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

I needed to create an xml source object so I could force an unmarshaller to return a specific type of object.

Source source = new StreamSource(new StringReader(myString));

September 3rd, 2012

Posted In: Java, JAXB

Leave a Comment

In a previous post, I showed how to use @XmlSeeAlso to inform the JAXB context of inheriting classes. This is great during marshalling. The real pain was on unmarshalling. A requirement we had was that there were two objects that would need to be able to be unmarshalled from the same root element, . This combined with the @XmlSeeAlso caused a problem. When I was unmarshalling an object of A, it consistently returned an object of type B. The only way I found to force it to return a certain object was to specify it via:

        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(...This.class or Super.class...);
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            Source source = new StreamSource(new StringReader(myString));

            JAXBElement<PersonWatchModel> jaxbElement = unmarshaller.unmarshal(source, This.class);
            someClass.setSomeProperty(jaxbElement.getValue());
        } catch (JAXBException e) {
            log.error("JAXBException {}", e);
            throw e;
        }

This forced it to a specific type and I could use the .getValue() property to get the java object that I needed.

September 3rd, 2012

Posted In: Java, JAXB, xml

Leave a Comment

I have a list in an object. I need to output this list in XML. Normally this would be accomplished by the following:

@XmlElementWrapper(name = "licenseList")
@XmlElement(name = "license")
public List<WatchLicense> getLicenses() {
    return licenses;
}

This produces the following output

<licenseList>
    <license>data</license>
    <license>data</license>
    <license>data</license>
</licenseList>

This is great, until I have the need to add an attribute to licenseList. You can’t add an attribute to a generated wrapper via annotations only. Enter the xml type adapter. There are three pieces. The defined type that is marshallable (LicenseListType), the annotation of the unmarshallable element with the xml type adapter and then the adapter itself (LicenseListAdapter).

First off is the new type. There are several things here. First off, it is a normal wrapper of the type that used to have to be used before @XmlElementWrapper. You used to have to add this to your class directly as a property. I didn’t want to because the names of the elements and their location had meaning to my views. Second is the addition of the getHashCode() method. This is the attribute that I needed to add. It uses the generated hashCode method.

package com.appriss.jxp.watchapi.jaxb;

import com.appriss.jxp.watchapi.WatchLicense;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.springframework.util.AutoPopulatingList;

import javax.xml.bind.annotation.XmlAttribute;

import java.util.List;

/**
 * Wrapper for list of license.  Normally, this would be handled by adding @XmlElementWrapper(name = "licenseList") to
 * the getter.  In this case, we also need to add a hashCode attribute the value and JAXB doesn't support an element
 * wrapper with an attribute.
 * @author nshelton
 */
public class LicenseListType {

    @XmlAttribute(name = "hashCode")
    public int getHashCode() {
        return hashCode();
    }

    private List<WatchLicense> license = new AutoPopulatingList<WatchLicense>(WatchLicense.class);

    public List<WatchLicense> getLicense() {
        return license;
    }

    public void setLicense(List<WatchLicense> license) {
        this.license = license;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(license).toHashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof LicenseListType) {
            final LicenseListType other = (LicenseListType) obj;
            return new EqualsBuilder().append(license, other.license).isEquals();
        } else {
            return false;
        }
    }
}

Next up is the adapter class which will directly use this new type. The adapter’s job is to translate from one unmarshallable object to a marshallable object.

package com.appriss.jxp.watchapi.jaxb;

import com.appriss.jxp.watchapi.WatchLicense;

import javax.xml.bind.annotation.adapters.XmlAdapter;

import java.util.LinkedList;
import java.util.List;

/**
 * License list type adapter to convert between a list of watch licenses and a license list type.  This would normally
 * be handled by @XmlElementWrapper, but we also need to specify an attribute and JAXB doesn't support attributes on an
 * annotation generated wrapper.
 * @author nshelton
 */
public class LicenseListAdapter extends XmlAdapter<LicenseListType, List<WatchLicense>> {

    /**
     * Convert a value type to a bound type.
     * @param licenseListType The value to be converted. Can be null.
     * @throws Exception if there's an error during the conversion. The caller is responsible for reporting the error to
     *                   the user through {@link javax.xml.bind.ValidationEventHandler}.
     */
    @Override
    public List<WatchLicense> unmarshal(LicenseListType licenseListType) throws Exception {
        List<WatchLicense> watchLicenses = new LinkedList<WatchLicense>();
        watchLicenses.addAll(licenseListType.getLicense());
        return watchLicenses;
    }

    /**
     * Convert a bound type to a value type.
     * @param watchLicenses The value to be converted. Can be null.
     * @throws Exception if there's an error during the conversion. The caller is responsible for reporting the error to
     *                   the user through {@link javax.xml.bind.ValidationEventHandler}.
     */
    @Override
    public LicenseListType marshal(List<WatchLicense> watchLicenses) throws Exception {
        LicenseListType licenseListType = new LicenseListType();
        licenseListType.setLicense(watchLicenses);
        return licenseListType;
    }
}

The magic happens in the marshall and unmarshall methods. In the marshall method, it accepts the unmarshallable class and returns a marshallable class. In the unmarshaller, it accepts the marshallable class and returns the unmarshallable class.

Now all that is left to do is to define the adapter class for a property. The @XmlJavaTypeAdapter annotation is used to identify the class that will be used to marshall/unmarshall this element.

@XmlJavaTypeAdapter(LicenseListAdapter.class)
public List<WatchLicense> getLicenses() {
    return licenses;
}

August 22nd, 2012

Posted In: Java, JAXB, json

Tags: , , ,

2 Comments

JAXB by default would output the value of a getter for property “oldName” as data. Specify @XmlElement if you want to use a different name.

@XmlElement(name ="newName")
public String getOldName() {
     return oldName;
}

Thanks to @XmlElement, it will be outputted as data.

This is especially useful when you have a list of objects. Normally you would have something like:

public List<String> getValues() {
     return values;
}

You would need a way to get the collection of data. This can be accomplished by adding @XmlElementWrapper.

@XmlElementWrapper(name = "valueList")
public List<String> getValues() {
     return values;
}

This would create the following output

<valueList>
     <values>value</values>
     <values>value</values>
     <values>value</values>
     <values>value</values>
</valueList>

This is correct, but I don’t like to have a plural name for a single value container. I could change the name of my property to be value, but that looks weird when I iterate over value in my code. A better solution is to specify a wrapper and an element.

@XmlElementWrapper(name = "valueList")
@XmlElement(name = "value")
public List<String> getValues() {
     return values;
}

This would create the following output

<valueList>
     <value>value</value>
     <value>value</value>
     <value>value</value>
     <value>value</value>
</valueList>

Everybody is happy. My code looks like it should and the xml looks like it should.

August 21st, 2012

Posted In: Java, JAXB, xml

Leave a Comment

It’s pretty hard to troubleshoot generated XML. JAXB offers the ability to output it’s generated XML with a formatted option. After you create the marshaller but before you marshall, tell the marshaller to output formatted xml by:

JAXBContext jaxbContext = JAXBContext.newInstance(Animal.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter stringWriter = new StringWriter();
marshaller.marshal(watchForm, stringWriter);

August 21st, 2012

Posted In: Java, JAXB, xml

Leave a Comment

It’s not uncommon to use superclasses to contain code that is used in multiple sub-classes. This prevents code duplication and allows the code to be maintained in one place instead of several. The normal way that you marshall JAXB classes will have your duplicating the marshaller. It is possible to use the superclass and only specify it once. If you have class Animal and it is extended by Cat and Dog. How would you marshall them without having to duplicate code?

JAXBContext jaxbContext = JAXBContext.newInstance(Animal.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter stringWriter = new StringWriter();
marshaller.marshal(watchForm, stringWriter);

You specify the parent class when you get the JAXB context instance. This will get you an error message saying that the particular instance (Dog or Cat) that you are trying to marshall isn’t known to this context. This is worked around by adding @XmlSeeAlso({Cat.class, Dog.class}) to Animal. This tells the JAXB context to also load their definitions.

August 21st, 2012

Posted In: Java, JAXB, xml

Leave a Comment

UPDATE: THANKS to attel in the comments below, THIS NOW WORKS. I was using the response context object, when I should have been using the request context.

I have a case where the test system is going to be different than the production system. I started with the following in my Springframework config file:

<!--
     Define a cxf endpoint based on client stub generated from a wsdl. It
     is important to provide serviceName and endpointName so the wsdl is
     not needed at runtime. As far as I know the serviceName and
     endpointName do not have to have a special convention but it is good
     practice to use the service namespace and Service Interface name in
     the names
 -->
<jaxws:client id="justiceXchangeService"
              serviceName="namespace:service name"
              endpointName="namespace:endpoint name"
              address="my test service address"
              serviceClass="some.package.ServiceClass">
    <jaxws:properties>
        <!--all validation is turned off-->
        <entry key="set-jaxb-validation-event-handler" value="false"/>
    </jaxws:properties>
</jaxws:client>

This would allow me to inject the client by:

    @SuppressWarnings("SpringJavaAutowiringInspection")
    @Autowired
    private ServiceClass serviceClass;

This worked until I had to dynamically specify the service address. I found some documentation that said I should do it like:

        BindingProvider bindingProvider = (BindingProvider) serviceClass;
        bindingProvider.getRequestContext()
                       .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "different service address");

UPDATE: THANKS to attel in the comments below, THIS NOW WORKS. I was using the response context object, when I should have been using the request context.

Unfortunately when I checked my logs, I saw that the address in the original request was still the address declared in the spring config file. Changing the binding didn’t work.

I finally ran across using a proxy. Now my spring context looks like:



This caused some changes in my code. The client is now injected via:

    @Autowired
    private JaxWsProxyFactoryBean jaxWsProxyFactoryBean;

The dynamic service is address is specifyied and the proxy used by:

        jaxWsProxyFactoryBean.setAddress("https://avsdsmv.dfa.arkansas.gov/Appriss/JXC");
        ServiceClass serviceClass = (serviceClass) jaxWsProxyFactoryBean.create();
        serviceClass.wsCall(...);

August 15th, 2012

Posted In: CXF, Java, JAXB, SOAP, Spring

Tags: , , , , ,

4 Comments

LinkedIn Auto Publish Powered By : XYZScripts.com