JAXB compiler is binding xs:boolean to Java Boolean wrapper class, instead of boolean primitive type

mdarwin picture mdarwin · Oct 23, 2012 · Viewed 19.9k times · Source

I'm migrating a project from JAXB 1.0 to JAXB 2.1 and I'm having problems with the datatype mapping.

I'm using the Ant xjc binding compiler, and I've successfully configured the global bindings such that (for example) xs:date maps to java.util.Calendar.

However I'm getting generated methods which return Boolean, whereas I want boolean.

Here is the complex type:

<xs:element name="usage-auth-rate-charge">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="service-id" type="xs:string"/>
            <xs:element name="pricepoint_custom_fields_required" type="xs:boolean" minOccurs="0"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>

And the generated class looks like this:

public class UsageAuthRateCharge {
........
public Boolean isPricepointCustomFieldsRequired() {
    return pricepointCustomFieldsRequired;
}

The problem is that although boxing will work, if the supplied XML doesn't contain a value for pricepoint_custom_fields_required, the class's Boolean field is null, instead of false. So I get NullPointerExceptions when doing something like this:

methodWhichTakesPrimitiveBooleanArg(myUsageAuthRateChargeInstance.isPricepointCustomFieldsRequired());

because it tries to unbox the Boolean passed in - except it's null.


I can't change the schema, and I can't adjust all the client code to do the null checks.

I've set the optionalProperty attribute in my binding.xml as follows:

<globalBindings optionalProperty="primitive">

In the spec, it says: "If the attribute’s value is "primitive", it binds as it did in JAXB 1.0"

Yet this is clearly not happening.

How can I solve this problem?

UPDATE:

This is now fixed in jaxb 2.2.9: https://java.net/jira/browse/JAXB/fixforversion/16850

Answer

bdoughan picture bdoughan · Dec 13, 2013

Problem

The reason you are getting Boolean instead of boolean is that you have minOccurs="0" in your element definition. A null value will be stored for an absent element.

<xs:element name="pricepoint_custom_fields_required" type="xs:boolean" minOccurs="0"/>

What the Solution Should Be

The solution should be an external binding file that indicates that a primitive type should be used for optional values (see section 7.5.1 of the JAXB 2.2 specification).

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    version="2.1">
    <jaxb:globalBindings optionalProperty="primitive"/>
</jaxb:bindings>

The external binding file is specified using the -b option with XJC.

xjc -b binding.xml my-schema.xsd

Why That Solution Does Not Work

The following bugs have been opened to track the issue with optionalProperty="primitive".

Workaround

If you don't like the way a class is generated in JAXB, then you can create a class yourself and have JAXB pull it in as part of the schema to class generation.

binding.xml

<jxb:bindings 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    version="2.1">

    <jxb:bindings schemaLocation="my-schema.xsd">
        <jxb:bindings node="//xs:element[@name='usage-auth-rate-charge']/complexType">
            <jxb:class ref="com.example.foo.UsageAuthRateCharge"/>
        </jxb:bindings>
    </jxb:bindings>
</jxb:bindings>

UsageAuthRateCharge

Create this class with the methods as you want them.

XJC Call

Use the -b flag on the XJC call to specify the binding.xml file

xjc -b binding.xml my-schema.xsd

UPDATE

What I was really hoping was that one of the readers might be a jaxb dev team member who could do the change easily in jaxb. User archenroot offered a possible solution in his comments on jaxb issue 927 (a duplicate of 926) which leads me to think that for the right person, it would be a simple job to fix jaxb

I'm the EclipseLink JAXB (MOXy) lead. I have reached out to my colleagues who do the JAXB reference implementation (they also work for Oracle). Below is the reply I got from them:

I’ve tested in trunk - and the problem exists. I’ll check code how easy is to fix it.

Hopefully you will be able to get a real fix for this issue.