Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Formatted XML output in CXF?

Tags:

java

xml

jax-rs

cxf

I know about the possibility how to turn on the formatting when using Marshaller. But I'm using Apache CXF (JAX-RS) and returning a response like return Response.ok(entity).build();.

I haven't found any option how to format the output. How can I do it?

like image 981
user219882 Avatar asked Apr 14 '12 11:04

user219882


2 Answers

First off, the way to get formatted output of XML is to set the right property on the marshaller (typically JAXB when working with CXF, which is OK since JAXB does a creditable job). That is, somewhere you're going to have something doing this:

marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

The issue is that you don't necessarily want to have all output formatted; it adds quite a bit to the overhead. Luckily, you're already producing an explicit Response, so we can just use more features of that:

Marshaller marshaller = JAXBContext.newInstance(entity.getClass()).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
marshaller.marshal(entity, sw);

return Response.ok(sw.toString(), MediaType.APPLICATION_XML_TYPE).build();

Another method is mentioned in this JIRA issue (itself closed, but that's not so much of an issue for you):

The workaround is to register a custom output handler which can check whatever custom query is used to request the optional indentation:

http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/FormatResponseHandler.java

JAXBElementProvider and JSONProvider are driven by the JAXB Marshaller so by default they check a Marshaller.JAXB_FORMATTED_OUTPUT property on the current message.

This leads to code like this:

public class FormattedJAXBInterceptor extends AbstractPhaseInterceptor<Message> {
    public FormattedJAXBInterceptor() {
        super(Phase.PRE_STREAM);
    }

    public void handleMessage(Message message) {
        message.put(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    }

    public void handleFault(Message message) {
        message.put(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    }
}

The CXF site discusses registration of interceptors.

like image 120
Donal Fellows Avatar answered Sep 25 '22 09:09

Donal Fellows


You could implement a MessageBodyWriter. This is a JAX-RS mechanism that allows you to override how an object is marshalled to XML.

package org.example;

import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;

import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;

@Provider
@Produces(MediaType.APPLICATION_XML)
public class FormattingWriter implements MessageBodyWriter<Object>{

    @Context
    protected Providers providers;

    public boolean isWriteable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    public void writeTo(Object object, Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType,
        MultivaluedMap<String, Object> httpHeaders,
        OutputStream entityStream) throws IOException,
        WebApplicationException {
        try {
            ContextResolver<JAXBContext> resolver 
                = providers.getContextResolver(JAXBContext.class, mediaType);
            JAXBContext jaxbContext;
            if(null == resolver || null == (jaxbContext = resolver.getContext(type))) {
                jaxbContext = JAXBContext.newInstance(type);
            }
            Marshaller m = jaxbContext.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            m.marshal(object, entityStream);
        } catch(JAXBException jaxbException) {
            throw new WebApplicationException(jaxbException);
        }
    }

    public long getSize(Object t, Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

}

For More Information

Below is a link to a complete example where a MessageBodyWriter is used as part of a JAX-RS service.

  • http://blog.bdoughan.com/2012/03/moxy-as-your-jax-rs-json-provider.html
like image 24
bdoughan Avatar answered Sep 25 '22 09:09

bdoughan