2

My problem is that I have a class of type MessageList, which contains Message items, defined as such:

<xsd:complexType name="MessageType">
    <xsd:sequence>
        <xsd:any/>
    </xsd:sequence>
</xsd:complexType>
<xsd:element name="Message" type="tns:MessageType"/>
<xsd:complexType name="MessageListType">
    <xsd:sequence>
        <xsd:element name="Message" maxOccurs="unbounded"/>
    </xsd:sequence>
</xsd:complexType>
<xsd:element name="MessageList" type="tns:MessageListType"/>

My message class looks something like(with getters and setters I've left out here)

public class Message
{
private String referenceId;
private String messageType;
private Timestamp createdTime;
private String transactionIdForeignKey;
private String xmlDetail;
}

When I try to marshal this class in my servlet like:

         List<Message> retrievedMessages = new ArrayList<Message>();
         retrievedMessages.add(new Message);
         retrievedMessages.add(new Message);

         MessageListType messageListType = new MessageListType();
         for(Message message: retrievedMessages){
             messageListType.getMessage().add(message);
         }

I get the following error:

[#|2013-04-25T16:43:40.434+0100|WARNING|sun-appserver2.1|javax.enterprise.system.stream.err|_ThreadID=188;_ThreadName=p: thread-pool-1; w: 76;_RequestID=40c6fe10-c12f-4d0b-8912-c960381910d7;|
javax.xml.ws.WebServiceException: javax.xml.bind.MarshalException
 - with linked exception:
[javax.xml.bind.JAXBException: class uk.co.test.test.audit.model.Message nor any of its super class is known to this context.]
    at com.sun.xml.ws.message.jaxb.JAXBMessage.writePayloadTo(JAXBMessage.java:322)
    at com.sun.enterprise.jbi.serviceengine.comm.WrappedMessage.writePayloadTo(WrappedMessage.java:419)
    at com.sun.enterprise.jbi.serviceengine.comm.WrappedMessage$DocumentStyleWrapper.wrap(WrappedMessage.java:215)
    at com.sun.enterprise.jbi.serviceengine.comm.WrappedMessage.wrap(WrappedMessage.java:170)
    at com.sun.enterprise.jbi.serviceengine.comm.ProviderInOut.send(ProviderInOut.java:143)
    at com.sun.enterprise.jbi.serviceengine.bridge.transport.NMRServerConnection.sendResponse(NMRServerConnection.java:81)
    at com.sun.enterprise.jbi.serviceengine.bridge.transport.JBIAdapter$WSToolkit.handle(JBIAdapter.java:127)
    at com.sun.enterprise.jbi.serviceengine.bridge.transport.JBIAdapter.handle(JBIAdapter.java:86)
    at com.sun.enterprise.jbi.serviceengine.bridge.JAXWSMessageProcessor.doWork(JAXWSMessageProcessor.java:93)
    at com.sun.corba.ee.impl.orbutil.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:555)
Caused by: javax.xml.bind.MarshalException
 - with linked exception:
[javax.xml.bind.JAXBException: class uk.co.test.test.audit.model.Message nor any of its super class is known to this context.]
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:282)
    at com.sun.xml.bind.v2.runtime.BridgeImpl.marshal(BridgeImpl.java:100)
    at com.sun.xml.bind.api.Bridge.marshal(Bridge.java:141)
    at com.sun.xml.ws.message.jaxb.JAXBMessage.writePayloadTo(JAXBMessage.java:315)

Anyone know why this is happening? I'm guessing the servlet that's doing the marshalling needs to know which class to look at but I'm not sure how to do this?

user898465
  • 944
  • 1
  • 12
  • 23
  • @bmorris591 already has a more thorough answer than I would provide, so I'll just comment. The short of is that the xmlAnyElement is just for unmarshalling. It's just a catch all for other elements in XML that the JAXB class doesn't have. You can't use it to marshall all the other attributes of a JAXB class. http://docs.oracle.com/javaee/5/api/javax/xml/bind/annotation/XmlAnyElement.html – Nick Roth Apr 25 '13 at 17:36

1 Answers1

5

You need to be more precise in your Schema if you want to be that precise in your class.

JAXB relies on the schema defining a structure that is the same as the structure you want JAXB to unmarshall to.

To illustrate, lets compile your schema using xjc - this will create the class that JAXB can umarshall to automatically and annotate it.

Here is the class:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "MessageType", propOrder = {
    "any"
})
public class MessageType {

    @XmlAnyElement(lax = true)
    protected Object any;

    public Object getAny() {
        return any;
    }

    public void setAny(Object value) {
        this.any = value;
    }

}

You can see that all JAXB can tell from your schema is that you have a MessageType class that contains an unknown amount of stuff - <xsd:any/> - there is no way for JAXB to determine that you, for example, have a String called referenceId that needs to be set.

If you can change your schema, then updating to

<xsd:complexType name="MessageType">
    <xsd:attribute name="referenceId" type="xsd:string"/>
    <xsd:attribute name="messageType" type="xsd:string"/>
    <xsd:attribute name="transactionIdForeignKey" type="xsd:string"/>
    <xsd:attribute name="xmlDetail" type="xsd:string"/>        
    <xsd:attribute name="createdTime" type="xsd:integer"/>        
</xsd:complexType>

Tells JAXB the information it needs to build the class (the TimeStamp is a long in this case - to unmarshall to a TimeStamp you need some custom logic).

The generated class now resembles your own class:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "MessageType")
public class MessageType {

    @XmlAttribute(name = "referenceId")
    protected String referenceId;
    @XmlAttribute(name = "messageType")
    protected String messageType;
    @XmlAttribute(name = "transactionIdForeignKey")
    protected String transactionIdForeignKey;
    @XmlAttribute(name = "xmlDetail")
    protected String xmlDetail;
    @XmlAttribute(name = "createdTime")
    protected BigInteger createdTime;

    public String getReferenceId() {
        return referenceId;
    }

    public void setReferenceId(String value) {
        this.referenceId = value;
    }

    public String getMessageType() {
        return messageType;
    }

    public void setMessageType(String value) {
        this.messageType = value;
    }

    public String getTransactionIdForeignKey() {
        return transactionIdForeignKey;
    }

    public void setTransactionIdForeignKey(String value) {
        this.transactionIdForeignKey = value;
    }

    public String getXmlDetail() {
        return xmlDetail;
    }

    public void setXmlDetail(String value) {
        this.xmlDetail = value;
    }

    public BigInteger getCreatedTime() {
        return createdTime;
    }

    public void setCreatedTime(BigInteger value) {
        this.createdTime = value;
    }

}

You can see that the attributes in the xml map directly to the fields in the class. You can also see the annotations that JAXB uses, these are not required but help to tell it what to map to where.

If you cannot change your schema then you will need to implement custom logic to search your <xsd:any/> for relevant properties and map then manually. This should get you started if you go down that route.

Community
  • 1
  • 1
Boris the Spider
  • 59,842
  • 6
  • 106
  • 166