Stephane Rolland
Stephane Rolland

Reputation: 39906

when does JAXB unmarshaller.unmarshal returns a JAXBElement<MySchemaObject> or a MySchemaObject?

I have two codes, in two different java projects, doing almost the same thing, (unmarshalling the input of a webservice according to an xsd-file).

But in one case I should write this: (Input is a placeholder name) ( element is OMElement input )

ClassLoader clInput = input.ObjectFactory.class.getClassLoader();
JAXBContext jc = JAXBContext.newInstance("input", clInput);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Input input = (Input)unmarshaller.unmarshal( element.getXMLStreamReader() );

and in the other lib I must use JAXBElement.getValue(), because it is a JAXBElement that is returned, and a simple (Input) cast simply crashes:

Input input = (Input)unmarshaller.unmarshal( element.getXMLStreamReader() ).getValue();

Do you know what leads to such a difference ?

Upvotes: 33

Views: 40260

Answers (5)

Stephane Rolland
Stephane Rolland

Reputation: 39906

Thanks to all your explanations and links, I've have written this so as to take care of both cases, using Annotation Introspection.

This has the advantage of:

  • not modifying generated java classes
  • allowing all possible xsd format

It works for output as well as input, and is according to me more generic:

public class JaxbWrapper {

    private static boolean isXmlRootElement(Class classT){
        
        Annotation[] annotations = classT.getAnnotations();
        
        for(Annotation annotation : annotations){
            if(annotation instanceof XmlRootElement){
                return true;
            }
        }       
        
        return false;
    }
    
    public static Object unmarshall(Class classObjectFactory, Class classObject, XMLStreamReader xmlStreamReader){
        
        Package pack = classObjectFactory.getPackage();
        String strPackageName = pack.getName();
        
        Object returnObject = null;
        
        try {
            JAXBContext jc = JAXBContext.newInstance(strPackageName, classObjectFactory.getClassLoader());
            
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            
            returnObject = unmarshaller.unmarshal( xmlStreamReader );
            
            boolean bIsRootedElement = isXmlRootElement(classObject);
            if(!bIsRootedElement)
            {
                JAXBElement jaxbElement = (JAXBElement) returnObject;
                returnObject = jaxbElement.getValue();              
            }
        }
        catch (JAXBException e) {
            /*...*/
        }   

        return returnObject;
    }
    
    private static void writeToXml(Class classObjectFactory, Object obj, XMLStreamWriter xmlStreamWriter){
        
        Package pack = classObjectFactory.getPackage();
        String strPackageName = pack.getName();
        
        try {       
            JAXBContext jc = JAXBContext.newInstance(strPackageName, classObjectFactory.getClassLoader());
            Marshaller marshaller = jc.createMarshaller();
            marshaller.marshal(obj, xmlStreamWriter);
        }
        catch(JAXBException e) {
            /*...*/
        }       
    }
    
    public static String marshall(Class classObjectFactory, Class classObject, Object obj){
        
        Object objectToMarshall = obj; 
        
        boolean bIsRootedElement = isXmlRootElement(classObject);
        if(!bIsRootedElement)
        {
            Package pack = classObjectFactory.getPackage();
            String strPackageName = pack.getName();
            
            String strClassName = classObject.getName();
            
            QName qName = new QName(strPackageName, strClassName);
            
            JAXBElement jaxbElement = new JAXBElement(qName, classObject, null, obj);
            
            objectToMarshall = jaxbElement; 
        }
        
        StringWriter sw = new StringWriter();
        XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
        XMLStreamWriter xmlStreamWriter = null;
        
        try {
            xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(sw);
            
            writeToXml(classObjectFactory, objectToMarshall, xmlStreamWriter);

            xmlStreamWriter.flush();
            xmlStreamWriter.close();
        } 
        catch (XMLStreamException e) {
            /*...*/
        }

        return sw.toString();
    }
}

Upvotes: 1

test7788
test7788

Reputation: 21

I have the same problem. JAXB unmarshaller.unmarshal returns a JAXBElement<MyObject> instead of desired MyObject.

I found and removed @XmlElementDecl. The problem is solved.

Upvotes: 0

bdoughan
bdoughan

Reputation: 149017

If the root element uniquely corresponds to a Java class then an instance of that class will be returned, and if not a JAXBElement will be returned.

If you want to ensure that you always get an instance of the domain object you can leverage the JAXBInstrospector. Below is an example.

Demo

package forum10243679;

import java.io.StringReader;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;

public class Demo {

    private static final String XML = "<root/>";

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        JAXBIntrospector jaxbIntrospector = jc.createJAXBIntrospector();

        Object object = unmarshaller.unmarshal(new StringReader(XML));
        System.out.println(object.getClass());
        System.out.println(jaxbIntrospector.getValue(object).getClass());

        Object jaxbElement = unmarshaller.unmarshal(new StreamSource(new StringReader(XML)), Root.class);
        System.out.println(jaxbElement.getClass());
        System.out.println(jaxbIntrospector.getValue(jaxbElement).getClass());
    }

}

Output

class forum10243679.Root
class forum10243679.Root
class javax.xml.bind.JAXBElement
class forum10243679.Root

Upvotes: 34

Piotr Kochański
Piotr Kochański

Reputation: 22672

You need to add to your JAXB generated class proper @XMLRootElement - it should have namespace:

@XmlRootElement(namespace="http://your.namespace.com/", name="yourRootElement")

Have a look at the related question (there are a lot of good tips): Class Cast Exception when trying to unmarshall xml?

Upvotes: 1

Puce
Puce

Reputation: 38132

It depends on the presence of XmlRootElement annotation on the class of your root element.

If you generate your JAXB classes from an XSD, the following rules are applied:

  • if the type of the root element is an anonymous type -> XmlRootElement annotation is added to the generated class
  • if the type of the root element is a top level type -> XmlRootElement annotation is omitted from the generated class

For that reason I often choose anonymous types for root elements.

You can customize the class name of this anonymous type with a customization file. E.g. create a bindings.xjc file like this:

<jxb:bindings version="1.0"
              xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
              xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <jxb:bindings schemaLocation="yourXsd.xsd" node="/xs:schema">
        <jxb:bindings  node="//xs:element[@name='yourRootElement']">
            <jxb:class name="YourRootElementType"/>
        </jxb:bindings> 
    </jxb:bindings>
</jxb:bindings>

Upvotes: 5

Related Questions