Search code examples
javaxmljaxbmarshalling

How do I dynamically add @XmlRoot tag from java?


I have a JAXB generated classes.The root tag class is Foo which is given below.

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "input"
})
@XmlRootElement(name = "Foo")
public class Foo {

    @XmlElement(name = "Input", required = true)
    protected Too input;

    public Too getInput() {
        return input;
    }

    public void setInput(Too value) {
        this.input = value;
    }
}

There is a sub tag class as below.

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Bar", propOrder = {
    "request"
})

public class Bar {

    @XmlElement(name = "Request", required = true)
    protected List<RequestType> request;

    public List<RequestType> getRequest() {
        if (request == null) {
            request = new ArrayList<RequestType>();
        }
        return this.request;
    }

}

There are some case where I need to construct an xml with Bar as a root class. I use Jaxb marshallar to convert from the object to string.

public static String jaxbObjectToXMLString(Object obj) {
        try {
            final Marshaller m = JAXBContext.newInstance(obj.getClass()).createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            final StringWriter w = new StringWriter();
            m.marshal(obj, w);
            return w.toString();
        } catch (JAXBException e) {
            logger.error("Returning empty xml string", e);
            return EMPTY_STR;
        }
    }

I can able to marshal If I mention @XmlRootElement(name = "Bar"). I dont want to add this in the class as I dont want Bar to be my root tag always. Is there a way that I can do this inside jaxbObjectToXMLString(Object obj) based on the object I am passing.

if(obj instanceof Bar) { dynamically make bar as XmlRoot and generate xml}
else if(obj instanceof Foo){ marshall as it is}

Solution

  • The usual way is to define more than one xs:element in your XML Schema and compile again (xjc).

     <xs:element name="Foo" type="Foo" />
     <xs:element name="Bar" type="Bar" />
    

    There is no problem with @XmlRootElement being on two classes.

    It's also possible to define a root element wrapped into a JAXBElement:

    <T> JAXBElement<T> wrap( String ns, String tag, T o ){
        QName qtag = new QName( ns, tag );
        Class<?> clazz = o.getClass();
        @SuppressWarnings( "unchecked" )
        JAXBElement<T> jbe = new JAXBElement( qtag, clazz, o );
        return jbe;
    }
    
    void marshal() throws Exception {
        Foo foo = new Foo();
        //...
        JAXBElement<Foo> jbe = wrap( "", "Foo", foo );
        JAXBContext jc = JAXBContext.newInstance( PACKAGE );
        Marshaller m = jc.createMarshaller();
        m.marshal( jbe, ... );
    }