JAX-RS Data BindingsPage added by Sergey Beryozkin
JAX-RS : Data Bindings
JAXB supportThe request and response can be marshalled and unmarshalled to/from Java object using JAXB. There's a number of ways to tell to the JAXB provider how objects can be serialized. The simplest way is to mark a given type with @XmlRootElement annotation. For example: @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: @Path("/customerservice/") public class CustomerService { @GET @Path("/customers/{customerId}/") public Customer getCustomer(@PathParam("customerId") String id) { .... } } The wire representation of Customer object is: <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: @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() { .... } } Alternatively to using @XmlRootElement and Collection wrappers, one can provide an Object factory which will tell JAXB how to Another option is to register one or more JAX-RS ContextResolver providers capable of creating JAXBContexts for a number of different types. The default JAXBElementProvider will check these resolvers first before attempting to create a JAXBContext on its own. Finally, JAXBProvider provides a support for serializing response types and deserializing parameters of methods annotated with @XmlJavaTypeAdapter annotations. Configuring JAXB providerThe default JAXB provider can be configured in a number of ways. For example, here's how to set up marshall properties : <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> Individual marshal properties can be injected as simple properties. At the moment, Marshaller.JAXB_SCHEMA_LOCATION can be injected as "schemaLocation" property. Schema validation can be enabled and custom @Consume and @Produce media types can be injected, see this example and "Customizing media types for message body providers" and "Schema Validation Support" sections for more information. One issue which one may need to be aware of it is that an exception may occur during the JAXB serialization process, after some content has already been processed and written to the output stream. By default, the output goes directly to the output HTTP stream so if an exception occurs midway through the process then the output will likely be malformed. If you set 'enableBuffering' property to 'true' then a JAXB provider will write to the efficient CXF CachedOutputStream instead and if an exception occurs then no text which has already been written will make it to the outside world and it will be only this exception that will be reported to the client. When enabling buffering, you can also control how the data being serialized will be buffered. By default, an instance of CXF CachedOutputStream will be used. If you set an "enableStreaming" property on the JAXBElementProvider then it will be a CXF CachingXMLEventWriter that will cache the serialization events. If you would like your own custom provider to write to a cached stream then you can either set an "org.apache.cxf.output.buffering" property to 'true' on a jaxrs endpoint or "enableBuffering" property on the provider. If this provider deals with XML and has a "getEnableStreaming" method returning 'true' then CachingXMLEventWriter will be used, in all other cases CachedOutputStream will be used. Please note that if you don't have wrapper types for your methods and the classloader you are using does not allow you to call defineClass(), you may need to set '-Dcom.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize' Marshall, unmarshall and context properties can be configured as well for both JAXB and JSON providers. JSON supportDefault JSON provider relies on Jettison 1.1 and it expects the types it deals with to follow the same techniques as described above in the JAXB support section for them to be handled properly. Following code returns a Customer object that is marshaled to JSON format: @Path("/customerservice/") public class CustomerService { @Produces("application/json") @GET @Path("/customers/{customerId}/") public Customer getCustomer(@PathParam("customerId") String id) { .... } Configuring JSON providerThe default JSON provider can be configured in a number of ways. For example, here's how to set up namespace-to-prefix mappings : <beans xmlns:util="http://www.springframework.org/schema/util"> <bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.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> Schema validation can be enabled and custom @Consume and @Produce media types can be injected, see this example and "Customizing media types for message body providers" and "Schema Validation Support" sections for more information. Dealing with JSON array serialization issuesThere is a well known problem in the JSON community which shows itself in the wrong serialization of List objects containing a single value only. To work around this issue, one needs to enable a 'serializeAsArray' feature on a JSONProvider, with the additional option of specifying the individual fields which needs to be processed accordingly using an 'arrayKeys' property. Please see this example for more information. Note that 'serializeAsArray' and 'arrayKeys' can be combined to produce so called natural convention sequences. For example, given the following two class definitions : @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; } } an instance of Post class can be serialized like this if a JSONProvider has had its 'serializeAsArray' property set to 'true' and 'arrayKeys' list property set to contain 'comments' value : > {"post":{"title":"post","comments":[{"title":"comment1"},{"title":"comment2"}]}} One other property which might help during the serialization is a boolean "ignoreMixedContent" property which lets to bypass a Jettison issue to do with outputting '$' properties when dealing with empty strings typically encountered in mixed content trees. You may request that JSONProvider ignores an 'xsi:type' attribute which is serialized in some cases by setting a "writeXsiType" boolean property with a 'false' value. You may also request that JSONProvider ignores all the namespaces during the serialization process by setting an "ignoreNamespaces" boolean property with a 'true' value. BadgerFish conventionStarting from CXF 2.2.5 it is possible to configure JSONProvider to support a BadgerFish convention. By default a "mapped" convention is supported, set a JSONProvider "convention" property with the value "badgerfish" if you'd like to work with the BadgerFish convention. Wrapping and Unwrapping JSON sequencesA "wrapperName" string property can be used to append a dropped root element name to an incoming JSON sequence for it to be deserialized properly. A "wrapperMap" map property can be used to nominate wrapper names for individual class names. In both cases, a 'supportUnwrapped' boolean property also has to be set. A boolean "dropRootName" property can be used to tell JSONProvider that a root element needs to be dropped. Common JAXB and JSON configurationAutomatic JAXBElement conversion during serializationIn some cases, wrapping object instances into JAXBElements may affect the way XML is produced. For example, given Base and Derived classes, returning an instance of Derived class, with Base one being a method response type, would produce an additional xsi:type attribute if this instance is wrapped into JAXBElement. One can set a "jaxbElementClassNames" list property which can contain class names like "org.foo.Base", etc. Handling JAXB beans without XmlRootElement annotationsA "jaxbElementClassNames" list property mentioned in the previous section can affect the serialization of objects of types with XmlRootElement annotations. In such cases, one might want to use a "jaxbElementClassMap" map property which contains class name to simple or expanded QName pairs. This will also lead to the automatic JAXBElement conversion durring the serialization. Finally, 'marshalAsJaxbElement' boolean property can be used when all the instances need to be wrapped - provided that simple class names of these instances can be used as element names. When deserializing, one can either update an existing ObjectFactory with methods returning JAXBElements or simply set an 'unmarshalFromJaxbElement' property on either JAXB or JSON provider. Handling explicit collectionsJAXB and JSON providers can handle explicit collections like List, Set or base Collection. 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 : <!-- 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> JSONProvider can only serialize explicit collections at the moment. If needed, it can be told to drop a collection element name using a boolean 'dropCollectionWrapperElementName'. For example, a 'dropCollectionWrapperElementName' and 'serializeAsArray' properties can be used to make the Dojo JSON RestStore consume the resulting JSON sequence (in CXF 2.2.5). Customizing JAXB XML and JSON input and outputSometimes you may want to adapt an incoming XML request or outgoing XML response. For example, your application has changed but a lot of legacy clients have not been updated yet. Another option is to register a custom JAXB or JSON provider extending CXF JAXBElementProvider or JSONProvider and overriding a method like createStreamWriter(). One can also use XSLTJaxbProvider to produce or modify the incoming XML. In fact, XSLTJaxbProvider can be used to adapt formats like JSON for legacy consumers. Please also see this overview of various related features available in CXF. In CXF 2.2.5, a new feature has been introduced whose goal is to generalize and simplify in a number of cases the way both JAXB and JSON can be customized. The following configuration properties have been added to the base JAXB/JSON AbstractJAXBProvider :
> book:thebook - change "book" to "thebook"
> book:thebook - append "book" before "thebook"
> index, {http://numbers}number - drop "index" and {http://numbers}number elements
The combination of "attributesAsElements" and "outDropElements" properties can be used to have certain attributes ignored in the output. This feature might be used in a number of cases. For example, one may have rootless JSON array collections such as "a:b},{c:d" deserialized into a bean by using a "wrapperName" JSONProvider property with a value like "list" which identifies a bean field and an "inAppendMap" property with a name of the bean (ex, "book") being appended before the "list", thus effectively turning the original JSON sequence into "{book:{list:a:b},{c:d}}". AtomCXF JAXRS offers users 3 options for dealing with Atom 1. Register Apache Abdera based Feed and/or Entry providers (org.apache.cxf.jaxrs.provider.AtomFeedProvider and org.apache.cxf.jaxrs.provider.AtomEntryProvider) with a jaxrs endpoint and have resource methods explicitly dealing with Abdera Feed or Entry classes. This is the most advanced option in that it lets users build Feeds or Entries in the way which suits most. Note that Abdera has not been actively mantained recently but practically speaking it is very good for working with most of the cases one may have to deal with when developing Atom-based applications. Both AtomFeedProvider and AtomEntryProvider support a 'formattedOutput' (pretty-printing) property. 2. Register an AtomPojoProvider injected with either AtomElementWriter or AtomElementReader implementations parameterized by either Abdera Feed or Entry and type of object which will have to be converted to/read from Feed/Entry. For example, BookAtomElementWriter\<Feed, Book> will be responsible for converting Book instances into Feeds while ChapterAtomElementWriter\<Entry, Chapter> will be responsible for converting Chapter instances into Entries. AtomElementWriter and AtomElementReader are injected using 'atomWriters' and 'atomReaders' map properties, where the keys are class names of the objects to be converted to/read from Feed or Entries, ex "org.apache.cxf.systest.jaxrs.Book". AtomPojoProvider offers users a way to have no Abdera Feed/Entry classes referenced from the 'main' application code, example : @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); } } Note that when an object such as Books is about to be converted to/read from Feed (which in our case is essentially a collection of entries each of them representing an individual Book) the AtomPojoProvider needs to know about the collection getters and/or setters so that it can create individual Entries. The "collectionGetters" and "collectionSetters" map properties with keys being the names of collection classes and values being the method names need to be used for providing this information, example a pair "org.apache.cxf.systest.jaxrs.Books:getBooks" tells AtomPojoProvider that when creating a Books Feed, the objects representing individual entries can be retrieved from Book with the help of "getBooks". If these properties are not set then AtomPojoProvider will try to get a method adding the simple class name to either 'get' or 'set', for example, an "org.apache.cxf.systest.jaxrs.Books:getBooks" pair is redundant if the Books class has a getBooks method. 3. This option is nearly identical to the option 2, except that users configure AtomPojoProvider with concrete implementations of either AbstractFeedBuilder or AbstractEntryBuilder. The difference is that in this case users have no explicit dependencies in their code on Atom-aware libraries such as Abdera - it may make it easier to experiment with various Atom libraries. Aegis Data BindingUse org.apache.cxf.provider.AegisElementProvider to start doing Aegis with JAX-RS XMLBeansUse org.apache.cxf.provider.XmlBeansElementProvider to start doing XmlBeans with JAX-RS CXF DataBindingsStarting from CXF 2.2.3 it is now possible to register a CXF DataBinding bean using a jaxrs:databinding element and it will be wrappped as a JAXRS MessageBodyReader/Writer DataBindingProvider capable of dealing with XML-based content. It can be of special interest to users combining JAX-RS and JAXWS. Thus CXF JAXB, Aegis, SDO and XMLBeans databindings can be easily plugged in. Similarly to the default JSONProvider the DataBindingJSONProvider JSON provider can have "namespaceMap", "serializeAsArray", "arrayKeys", "dropRootElement" and "writeXsiType" properties set. Additionally it may also have an "ignoreMixedContent" property set. Note that at the moment this feature is only available on the trunk. JAXB and Aegis DataBindings (XML only) is supported in CXF 2.2.3. Schema validationThere're two ways you can enable a schema validation. 1. Using jaxrs:schemaLocations element : <beans xmlns:util="http://www.springframework.org/schema/util"> <jaxrs:server address="/"> <jaxrs:schemaLocations> <jaxrs:schemaLocation>classpath:/schemas/a.xsd</jaxrs:schemaLocation> <jaxrs:schemaLocation>classpath:/schemas/b.xsd</jaxrs:schemaLocation> </jaxrs:schemaLocations> </jaxrs:server> /<beans> Using this option is handy when you have multiple bindings involved which support the schema validation. In this case 2. Configuring providers individually Please see this example of how both JAXB and JSON providers are using a shared schema validation configuration. Fast InfosetYou can enable the FastInfoset by explicitly registering CXF FastInfoset interceptors with a JAXRS endpoint and configuring JAXBElementProvider to support an "application/fastinfoset" media type : <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"/>
Change Notification Preferences
View Online
|
Add Comment
|
- [CONF] Apache CXF Documentation > JAX-RS Data Bindings confluence
