Search code examples
jsonrestjaxbcxf

JSON Provider builds wrong String field, when value is 'true' or 'false'


I have a problem. Could you please help me. I have an application: CXF+Spring+JAXB+REST. And I try to generate Response using JSONProvider class There is a bean class:

@XmlRootElement
class Foo {
@XmlElement
private String bar;
}

When I set to the field value:

setBar("true")

or

setBar("false");

JSONProvider returns to me:

"bar":false

But, I expect

"bar":"false"

Because I use String type. What should I do with that?


Solution

  • Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

    The JAXB (JSR-222) specification does not cover JSON-binding. In a REST/JAX-RS context it is up to the provider to apply the JAXB mapping rules to a JSON representation. There are 3 different approaches that are being used today:

    1. Use a JAXB implementation with a library like Jettison. Jettison implements the StAX interfaces but interprets JSON. The advantage of this approach is that it can be used with any JAXB implementation to produce/consume JSON, the disadvantage of this approach is that Jettison does not have enough information to do a perfect JSON representation. Jettison receives the String "true" without knowing what the corresponding Java type was, so it decides to represent it as a JSON boolean, since it is probably the desired output most of the time.
    2. REST/JAX-RS implementation leverages a specific JAXB implementation to produce JSON. This approach is similar to the first, except that the REST/JAX-RS provider makes proprietary calls to the JAXB provider to make more educated guesses to what the JSON representation should be.
    3. The JAXB implementation provides the JSON-binding implementation. Currently EclipseLink MOXy is the only JAXB implementation to support JSON-binding.

    MOXy EXAMPLE

    Root

    Below is a domain object with String and boolean fields. I've also added fields annotated with @XmlSchemaType which can be used to override the default representation.

    package forum11145933;
    
    import javax.xml.bind.annotation.*;
    
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Root {
    
        String barString;
    
        boolean barBoolean;
    
        @XmlSchemaType(name="boolean")
        String barStringWithXmlTypeBoolean;
    
        @XmlSchemaType(name="string")
        boolean barBooleanWithXmlTypeString;
    
    }
    

    jaxb.properties

    To specify MOXy as your JAXB provider you need to add a file called jaxb.properties in the same package as your domain model with the following entry:

    javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
    

    Demo

    The following code demonstrates how to marshal the domain object to JSON. Note that there are no compile time dependencies on MOXy.

    package forum11145933;
    
    import javax.xml.bind.*;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(Root.class);
    
            Root root = new Root();
            root.barString = "true";
            root.barBoolean = true;
            root.barStringWithXmlTypeBoolean = "true";
            root.barBooleanWithXmlTypeString = true;
    
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty("eclipselink.media-type", "application/json");
            marshaller.marshal(root, System.out);
        }
    
    }
    

    Output

    The following is the output from running the Demo code. Note how the boolean and String properties are written out correctly. Also notice how the use of the @XmlSchemaType annotation allows us to marshal a boolean as a String and vice versa.

    {
       "root" : {
          "barString" : "true",
          "barBoolean" : true,
          "barStringWithXmlTypeBoolean" : true,
          "barBooleanWithXmlTypeString" : "true"
       }
    }
    

    MOXy & JAX-RS

    MOXy includes MOXyJsonProvider which can be used to enable MOXy as the JSON provider in your JAX-RS environment: