Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

 

 

 

Wiki Markup
{span:style=font-size:2em;font-weight:bold} JAX-RS : Data Bindings {span}

 

 

 

Table of Contents

JAXB support

...

For example:

Code Block
java
java

@XmlRootElement(name = "Customer")
public class Customer {
    private String name;
    private long id;

    public Customer() {
    }

    public void setName(String n) {
        name = n;
    }

    public String getName() {
        return name;
    }

    public void setId(long i) {
        id = i;
    }

    public long getId() {
        return id;
    }
}

In the example below, the Customer object returned by getCustomer is marshaled using JAXB data binding:

Code Block
java
java

@Path("/customerservice/")
public class CustomerService {
    @GET
    @Path("/customers/{customerId}/")
    public Customer getCustomer(@PathParam("customerId") String id) {
        ....
    }
}

The wire representation of Customer object is:

Code Block
xml
xml

<Customer>
    <id>123</id>
    <name>John</name>
</Customer>

The simplest way to work with the collections is to define a type representing a collection. For example:

Code Block

@XmlRootElement(name = "Customers")
public class Customers {
    private Collection<Customer> customers;

    public Collection<Customer> getCustomer() {
        return customers;
    }

    public void setCustomer(Collection<Customer> c) {
        this.customers = c;
    }
}
@Path("/customerservice/")
public class CustomerService {
    @GET
    @Path("/customers/")
    public Customers getCustomers() {
        ....
    }
}

...

The default JAXB provider can be configured in a number of ways. For example, here's how to set up marshall properties :

Code Block
xml
xml

<beans xmlns:util="http://www.springframework.org/schema/util">
<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
<property name="marshallerProperties" ref="propertiesMap"/>
</bean>
<util:map id="propertiesMap">
<entry key="jaxb.formatted.output">
   <value type="java.lang.Boolean">true</value>
</entry>
</util:map>
/<beans>

...

For JAXBElementProvider to support Moxy a custom Moxy-aware JAX-RS ContextProvider implementation needs to be registered in jaxrs:providers. For example:

Code Block
java
java

import javax.xml.bind.JAXBContext;
import javax.ws.rs.ext.ContextResolver;
import org.eclipse.persistence.jaxb.JAXBContextFactory;

public class MoxyJaxbContextResolved implements ContextResolver<JAXBContext> {

  org.eclipse.persistence.jaxb.JAXBContextFactory factory = new JAXBContextFactory();

  public JAXBContext getContext(Class<?> cls) {
       return factory.createContext(cls);
  }

}

...

Following code returns a Customer object that is marshaled to JSON format:

Code Block

@Path("/customerservice/")
public class CustomerService {
    @Produces("application/json")
    @GET
    @Path("/customers/{customerId}/")
    public Customer getCustomer(@PathParam("customerId") String id) {
        ....
    }

...

The default JSON provider can be configured in a number of ways. For example, here's how to set up namespace-to-prefix mappings :

Code Block
xml
xml

<beans xmlns:util="http://www.springframework.org/schema/util">
<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
<property name="namespaceMap" ref="jsonNamespaceMap"/>
</bean>
<util:map id="jsonNamespaceMap" map-class="java.util.Hashtable">
<entry key="http://www.example.org/books" value="b"/>
</util:map>
/<beans>

...

Note that 'serializeAsArray' and 'arrayKeys' can be combined to produce so called natural convention sequences. For example, given the following two class definitions :

Code Block
java
java

@XmlRootElement()
@XmlType(name = "", propOrder = {"title", "comments" })
public static class Post {
    private String title;
    private List<Comment> comments = new ArrayList<Comment>();
    public void setTitle(String title) {
        this.title = title;
    }
    public String getTitle() {
        return title;
    }
    public void setComments(List<Comment> comments) {
        this.comments = comments;
    }
    public List<Comment> getComments() {
        return comments;
    }
}
   
public static class Comment {
     private String title;

     public void setTitle(String title) {
        this.title = title;
     }

     public String getTitle() {
         return title;
     }
} 

...

If you prefer working with Jackson JSON providers then register either JacksonJsonProvider:

Code Block
xml
xml

<jaxrs:providers>
   <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/>
</jaxrs:providers>

or JacksonJaxbJsonProvider (when working with JAXB beans):

Code Block
xml
xml

<jaxrs:providers>
   <bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
</jaxrs:providers>

and add this Maven dependency:

Code Block
xml
xml

<dependency>
  <groupId>org.codehaus.jackson</groupId>
  <artifactId>jackson-jaxrs</artifactId>
  <version>1.9.0</version>
</dependency>

...

By default JAXBContexts are created on demand. Starting from CXF 2.3.2 and 2.4.0 it is possible to configure JAXB-based providers to support the creation of a single JAXBContext, example :

Code Block
xml
xml

<bean id="jaxb" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
      <property name="singleJaxbContext" value="true"/>
      <property name="extraClass">
         <list>
           <value>org.apache.cxf.jaxrs.resources.SuperBook</value>
           <value>org.apache.cxf.jaxrs.resources.SuperBook</value>  
         </list>
      </property>
</bean>

...

There's also a 'collectionWrapperMap' property available for use in more advanced cases, when collections of different types are used, for example, when mixed collections of objects descended from abstract classes having no @XmlRootElement tags are returned :

Code Block
xml
xml

<!-- Configure JAXB Provider -->
<bean id="jaxbProvider"
class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
  <property name="collectionWrapperMap">
  <map>
    <entry>
      <key><value>com.foo.bar.MyObject</value></key>
      <value>MyObjects</value>
    </entry>
   </map>
   </property>
</bean> 

...

  • "outTransformElements" map property: can be used to change the output element names and change or drop namespaces; keys are the elements to be changed, values are the new element names. Examples:
Code Block
xml
xml

<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
  <property name="outTransformElements">
    <map>
      <!-- change "book" to "thebook" -->
      <entry key="book" value="thebook"/>
      
      <!-- drop the namespace from "book" -->
      <entry key="{http://books}book" value="book"/> 
      
      <!-- qualify "book" with "http://books" -->
      <entry key="book" value="{http://books}thebook"/> 
      
      <!--  change namespace to "http://books" for all the elements with the "http://book" namespace -->
      <entry key="{http://book}*" value="{http://books}*"/> 
    </map>
  </property>
</bean> 

...

  • "outAppendElements" map property: can be used to append new simple or qualified elements to the output; keys are the new elements, values are the elements the new ones will be appended before. Examples:
Code Block
xml
xml

<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
  <property name="outAppendElements">
    <map>
      <!-- append "book" before "thebook" -->
      <entry key="book" value="thebook"/>
      
      <!-- qualify "book" with "http://books" -->
      <entry key="{http://books}book" value="book"/> 
      
      <!-- drop the namespace from the "book" -->
      <entry key="book" value="{http://books}thebook"/> 
      
      <!--  change namespace to "http://book" for all the elements with the "http://books" namespace -->
      <entry key="{http://book}*" value="{http://books}*"/> 
    </map>
  </property>
</bean> 

...

  • "outDropElements" list property : can be used to drop elements during the serialization; note that children elements if any of a given dropped element are not affected. Examples:
Code Block
xml
xml

<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
  <property name="outDropElements">
    <list>
      <!-- ignore drop and {http://numbers}number elements -->
      <value>{http://numbers}number</value>
      <value>index</value>
    </list>
  </property>
</bean> 

...

Additionally it is possible to configure JAXBElementProvider or JSONProvider with contextual properties or DocumentDepthProperties:

Code Block
xml
xml


<bean id="depthProperties" class="org.apache.cxf.staxutils.DocumentDepthProperties">
  <property name="innerElementCountThreshold" value="500"/>
</bean> 

<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
  <property name="depthProperties" ref="depthProperties"/>
</bean> 

<bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
  <property name="depthProperties" ref="depthProperties"/>
</bean> 

<jaxrs:server id="endpoint1">
  <jaxrs:serviceBeans>
    <bean class="my.package.RootResource"/>
  </jaxrs:serviceBeans>
  <jaxrs:providers>
    <ref bean="jaxbProvider"/>
    <ref bean="jsonProvider"/>
  </jaxrs:providers>
</jaxrs:server>

<jaxrs:server id="endpoint2">
  <jaxrs:serviceBeans>
    <bean class="my.package.RootResource"/>
  </jaxrs:serviceBeans>
  <jaxrs:properties>
    <entry key="depthInnerElementCountThreshold" value="500"/
  </jaxrs:properties>
</jaxrs:server>


...

One option for supporting "JSON With Padding" (JSONP) is to extend the default JSONProvider and override its writeTo method as follows:

Code Block
java
java


@Produces("application/javascript")
class JsonpProvider extends JSONProvider {
		
    @Override
    public void writeTo(Object obj, Class<?> cls, Type genericType, Annotation[] anns, MediaType m, MultivaluedMap<String, Object> headers,
	OutputStream os) throws IOException {
	String prefix = getContext().getHttpServletRequest().getParameter("_jsonp");
	boolean hasPrefix = !isEmpty(prefix);
	if(hasPrefix) {
	    os.write(prefix.getBytes(HttpUtils.getSetEncoding(m, headers, "UTF-8")));
	    os.write('(');
	}
	super.writeTo(obj, cls, genericType, anns, m, headers, os);
	if(hasPrefix) {
		os.write(')');
	}
    }
}

...

Starting from CXF 2.3.4 and 2.4.0, JSONP can be supported with the help of CXF in/out interceptors:

Code Block
xml
xml

<jaxrs:server id="bookJsonp" address="/jsonp">
    <jaxrs:serviceBeans>
      <ref bean="serviceBean" />
    </jaxrs:serviceBeans>		  

    <jaxrs:inInterceptors>
       <bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpInInterceptor"/>
    </jaxrs:inInterceptors>
    <jaxrs:outInterceptors>
       <bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpPreStreamInterceptor"/>
       <bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpPostStreamInterceptor"/>
    </jaxrs:outInterceptors> 
</jaxrs:server>

JsonpInInterceptor checks if a JSONP callback query parameter is available (default is _jsonp) and if yes then it saves its value on the current exchange for out interceptors to know if they have to write the paddings or not. The name of the expected callback parameter can be customized. JsonpPreStreamInterceptor and JsonpPostStreamInterceptor ensure the actual JSON stream is 'padded' properly. JsonpPreStreamInterceptor will also set Content-Type to "application/x+javascript" by default but this can be changed:

Code Block
xml
xml

<jaxrs:server id="bookJsonp" address="/jsonp">
    <jaxrs:serviceBeans>
      <ref bean="serviceBean" />
    </jaxrs:serviceBeans>		  

    <jaxrs:inInterceptors>
       <bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpInInterceptor">
          <property name="callbackParam" value="myjsonp"/>
       </bean> 
    </jaxrs:inInterceptors>
    <jaxrs:outInterceptors>
       <bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpPreStreamInterceptor">
           <property name="mediaType" value="text/x+javascript"/>
       </bean>
       <bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpPostStreamInterceptor">
           <!-- default is ');' -->
           <property name="paddingEnd" value=")"/>
       </bean>
    </jaxrs:outInterceptors> 
</jaxrs:server>

...

Form payload is a sequence of name and value pairs, example, "name=Barry&age=20".
One can capture the form data by using either JAX-RS FormParam annotation or MultivaluedMap, for example:

Code Block
java
java


import javax.ws.rs.FormParam;
import javax.ws.rs.MultivaluedMap;

@Path("resource")
public class FormResource {

   @POST
   @Path("/form1")
   @Consumes("application/x-www-form-urlencoded")
   public void form1(@FormParam("name") String name, @FormParam("age") int age) {
   }

   @POST
   @Path("/form2")
   @Consumes("application/x-www-form-urlencoded")
   public void form1(MultivaluedMap<String, String> params) {
       String name = params.getFirst("name");
       String age = params.getFirst("age");
   }

}

...

AtomPojoProvider offers users a way to have no Abdera Feed/Entry classes referenced from the 'main' application code, example :

Code Block
java
java

@Path("books")
public class BooksRootResource {

 private Books books;

 @GET
 @Produces({"application/xml", "application/json", "application/atom+xml;type=feed"})
 public Books getCollectionOfBooks() {
     return books;
 } 

 @GET
 @Produces({"application/xml", "application/json", "application/atom+xml;type=entry"})
 @Path("{id}")
 public Book getBook(@PathParam("id") Long id) {
     return books.get(id);
 }

}

...

org.apache.cxf.jaxrs.provider.JAXRSDataBinding is a CXF DataBinding implementation which wraps JAX-RS providers and can be used by CXF JAX-WS endpoints thus making it possible to use JAX-RS providers for reading and writing XML payloads during SOAP and RESTful invocations. Example :

Code Block
xml
xml

  <jaxrs:server id="hello_rest" address="/hello-rest">
    <jaxrs:serviceBeans>
      <bean class="org.apache.cxf.systest.jaxrs.jaxws.HelloWorldImpl"/>
    </jaxrs:serviceBeans>

    <jaxrs:providers>
      <ref bean="jaxbProviderSoap"/>
    </jaxrs:providers>
  </jaxrs:server>

  <jaxws:endpoint xmlns:s="http://hello.com"
      serviceName="s:HelloWorld"
      endpointName="s:HelloWorldPort"
      id="hello_soap-databinding"
      implementor="org.apache.cxf.systest.jaxrs.jaxws.HelloWorldImpl"
      address="/hello-soap-databinding">
      
      <jaxws:dataBinding>
          <ref bean="jaxrs-data-binding"/>
      </jaxws:dataBinding>
      
  </jaxws:endpoint>        
 
  <bean id="jaxrs-data-binding" class="org.apache.cxf.jaxrs.provider.JAXRSDataBinding">
      <property name="provider" ref="jaxbProviderSoap"/>
  </bean>

  <bean id="jaxbProviderSoap" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider"/>

...

If you would like to write your own custom XML provider and have it wrapped by the JAXRSDataBinding then custom MessageBodyReader and MessageBodyWriter implementations should be ready to accept null InputStream and OutputStream parameters and retrieve XMLStreamReader and XMLStreamWriter instances from the current CXF Message. To make the custom provider as portable as possible, one may want to follow the following approach :

Code Block
java
java


public class PortableXMLProvider implements MessageBodyReader, MessageBodyWriter {

    public Object readFrom(Class<Object> type, Type genericType, Annotation[] anns, MediaType mt, 
                           MultivaluedMap<String, String> headers, InputStream is) 
                           throws IOException {

        XMLStreamReader reader = createReaderUsingStandardStaxApi(is);
        return readFromStream(reader);  

    }

    public void writeTo(Object obj, Class<?> cls, Type genericType, Annotation[] anns,  
                        MediaType m, MultivaluedMap<String, Object> headers, OutputStream os)
                        throws IOException {
        XMLStreamWriter writer = createWriterUsingStandardStaxApi(os);
        writeToStream(writer);  
    }

    // add more parameters if needed
    protected Object readFromStream(XMLStreamReader reader) {
        // read from XMLStreamReader
    }
    
    // add more parameters if needed
    protected void writeToStream(XMLStreamWriter writer) {
        // write to XMLStreamWriter
    }

    
}

public class CXFCustomXMLProvider extends PortableXMLProvider {

    @Override
    public Object readFrom(Class<Object> type, Type genericType, Annotation[] anns, MediaType mt, 
                           MultivaluedMap<String, String> headers, InputStream is) 
                           throws IOException {

        XMLStreamReader reader = getStaxHandlerFromCurrentMessage(XMLStreamReader.class);
        return readFromStream(reader);  

    }

    @Override
    public void writeTo(Object obj, Class<?> cls, Type genericType, Annotation[] anns,  
                        MediaType m, MultivaluedMap<String, Object> headers, OutputStream os)
                        throws IOException {
        XMLStreamWriter writer = getStaxHandlerFromCurrentMessage(XMLStreamWriter.class);
        writeToStream(writer);  
    }


    protected <T> T getStaxHandlerFromCurrentMessage(Class<T> staxCls) {
        Message m = PhaseInterceptorChain.getCurrentMessage();
        if (m != null) {
            return staxCls.cast(m.getContent(staxCls));
        }
        return null;
    }
}

...

1. Using jaxrs:schemaLocations element

Code Block
xml
xml

<beans>
<jaxrs:server address="/" serviceClass="org.apache.cxf.systest.jaxrs.BookStore">
  <jaxrs:schemaLocations>
     <jaxrs:schemaLocation>classpath:/schemas/a.xsd</jaxrs:schemaLocation>
     <jaxrs:schemaLocation>classpath:/schemas/b.xsd</jaxrs:schemaLocation>
     <!-- Possible from CXF 3.0.0 milestone2: -->
     <!-- 
         <jaxrs:schemaLocation>classpath:/schemas/</jaxrs:schemaLocation>
     -->  
  </jaxrs:schemaLocations>
</jaxrs:server>
</beans>

...

JAXB and JSON providers can be configured explicitly, for example:

Code Block
xml
xml

<beans xmlns:util="http://www.springframework.org/schema/util">
<jaxrs:server address="/" serviceClass="org.apache.cxf.systest.jaxrs.BookStore">
 <jaxrs:providers>
   <ref bean="jaxbProvider"/>
 </jaxrs:providers>
</jaxrs:server>

<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
   <property name="schemaLocations" ref="schemaHolder"/>
</bean>
  
<util:list id="theSchemas">
  <value>classpath:/WEB-INF/schemas/bookid.xsd</value>
  <value>classpath:/org/apache/cxf/systest/jaxrs/resources/book.xsd</value>
</util:list>
</beans>

If you have both JAXB and JSON providers validating the input data then you can get schemas shared between them, for example:

Code Block
xml
xml

<beans xmlns:util="http://www.springframework.org/schema/util">
 <jaxrs:server address="/" serviceClass="org.apache.cxf.systest.jaxrs.BookStore">
  <jaxrs:providers>
   <ref bean="jaxbProvider"/>
   <ref bean="jsonProvider"/>
  </jaxrs:providers>
 </jaxrs:server>

<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
   <property name="schemaHandler" ref="schemaHolder"/>
</bean>

<bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
   <property name="schemaHandler" ref="schemaHolder"/>
</bean>
  
<bean id="schemaHolder" class="org.apache.cxf.jaxrs.utils.schemas.SchemaHandler">
   <property name="schemas" ref="theSchemas"/>
</bean>
  
<util:list id="theSchemas">
  <value>classpath:/WEB-INF/schemas/bookid.xsd</value>
  <value>classpath:/org/apache/cxf/systest/jaxrs/resources/book.xsd</value>
</util:list>
</beans>

3. Using SchemaValidation annotation 

Starting from CXF 3.0.0 it is possible to use org.apache.cxf.annotations.SchemaValidation with JAX-RS root resources. Set its optional "schemas" property.

Support for catalogs

Available starting from CXF 2.5.5, 2.6.2

XML Catalogs can be used for the main schema (which is used to validate the data) to get the imported or included schema resources resolved locally.
By default, a "META-INF/jax-rs-catalog.xml" will be checked however the catalog location can be set either on JAXBElementProvider or JSONProvider:

Code Block
xml
xml

<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
   <property name="catalogLocation" value="classpath:/schemas/mycatalog.xml"/>
</bean>

where mycatalog.xml may look like this:

Code Block
xml
xml

<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
    <system systemId="http://schemas/bookid.xsd" uri="classpath:WEB-INF/schemas/bookid.xsd"/>
</catalog>

...

You can enable the FastInfoset by explicitly registering CXF FastInfoset interceptors with a JAXRS endpoint and configuring JAXBElementProvider to support an "application/fastinfoset" media type :
for example :

Code Block
xml
xml


<jaxrs:server id="restservice3" address="/rest3">

 <jaxrs:serviceBeans>
  <ref bean="bookstore"/>
 </jaxrs:serviceBeans>

 <jaxrs:providers>
  <ref bean="jaxbProvider"/>
 </jaxrs:providers>

 <jaxrs:outInterceptors>
  <ref bean="fastInfosetOutInterceptor"/>
 </jaxrs:outInterceptors>

 <jaxrs:inInterceptors>
  <ref bean="fastInfosetInInterceptor"/>
 </jaxrs:inInterceptors>

 <jaxrs:properties>
  <entry key="org.apache.cxf.endpoint.private" value="true"/>
 </jaxrs:properties>
</jaxrs:server>

<util:list id="fastinfosetType">
  <value>application/fastinfoset</value>
</util:list>

<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
  <property name="produceMediaTypes" ref="fastinfosetType"/>
  <property name="consumeMediaTypes" ref="fastinfosetType"/>
</bean>
<bean id="fastInfosetOutInterceptor" class="org.apache.cxf.interceptor.FIStaxOutInterceptor"/>
<bean id="fastInfosetInInterceptor" class="org.apache.cxf.interceptor.FIStaxInInterceptor"/>

...