Dennis
Dennis

Reputation: 3731

JAXB: Place XML tag on abstract classes

I am using an interface from a third party and they have a peculiar (to my mind at least) XML format for request/response and notification. The XML needs to look like this:

<Message>
  <Version>3.1</Version>
  <Request>
    <Request_Name>
      <RequestID> Request ID </RequestID>
      ...
    </Request_Name>
  </Request>
</Message>

The Request tag could also be one of Notification or Response. All Messages will have only one of these three. Request and Response both have RequestID. Response has ReturnValue as well. Notifications have a Session.

In order to prevent duplication I wanted to create an abstract class for each of these three types, and it almost works! What I can marshall to and unmarshall from looks like this:

<Message>
  <Version>3.1</Version>
  <Request_Name>
    <RequestID> Request ID </RequestID>
    ...
  </Request_Name>
</Message>

Note the missing Request tag! Same for Notification and Response. All information is there but these wrapper tags are killing me!

The Question: Can I put an XML tag on the abstract class? Or do I need make it non-abstract and create another top-level abstract class as the "real" abstract base class?

I haven't much experience with JAXB, so I'm hoping there is a simple way to do this. I tried making the request field on message an XMLElement instead but that only changes the Request_Name tag to Request and adds a type specification.

My code is basically as below, with setters and CTORs taken out for brevity.

The base Message class:

@XmlRootElement(name = "Message")
public class Message {
    @XmlElement(required = true, name = "Version") private String version;
    private Request request;
    private Response response;
    private Notification notification;

    @XmlElementRef(required = false)
    public Request getRequest() {
        return request;
    }

    @XmlElementRef(required = false)
    public Response getResponse() {
        return response;
    }

    @XmlElementRef(required = false)
    public Notification getNotification() {
        return notification;
    }
}

Abstract Request class. Similar for Notification and Response:

@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public abstract class Request {
    private String requestId;

    @XmlElement(name = "RequestID", required = true)
    public String getRequestId() {
        return requestId;
    }
}

An example Request implementation:

@XmlRootElement(name = "RegForAllNotificationsRequest")
public class RegForAllNotificationsRequest extends Request {
    private long session;
    private boolean register;

    @XmlElement(name = "SessionToUse")
    public long getSession() {
        return session;
    }
    @XmlElement(name = "Register")
    public boolean isRegister() {
        return register;
    }
}

The above gets marshalled something like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Message>
    <Version>8.3</Version>
    <RegForAllNotificationsRequest>
        <RequestID>94668</RequestID>
        <SessionToUse>548248</SessionToUse>
        <Register>true</Register>
    </RegForAllNotificationsRequest>
</Message>

Where I need this:

<?xml version="1.0" encoding="UTF-8"?>
<Message>
    <Version>8.3</Version>
    <Request>
        <RegForAllNotificationsRequest>
            <RequestID>94668</RequestID>
            <SessionToUse>548248</SessionToUse>
            <Register>true</Register>
        </RegForAllNotificationsRequest>
    </Request>
</Message>

Upvotes: 1

Views: 249

Answers (1)

Priyesh
Priyesh

Reputation: 2071

What you need here is a RequestContainer class that has the tag name Request. This class will contain the actual request implementation. This is required because, JAXB will render each class as a separate tag. The tag hierarchy is not built based on the class hierarchy.

The container class will look something like this:

@XmlRootElement(name = "Request")
public class RequestContainer {
   private Request request;
}

Similarly, for the Notification and Response. The Message class will contain an instance of the container classes for each like below:

@XmlRootElement(name = "Message")
public class Message {
    @XmlElement(required = true, name = "Version") private String version;
    private RequestContainer request;
    private ResponseContainer response;
    private NotificationContainer notification;

    @XmlElementRef(required = false)
    public Request getRequest() {
        return request;
    }

    @XmlElementRef(required = false)
    public Response getResponse() {
        return response;
    }

    @XmlElementRef(required = false)
    public Notification getNotification() {
        return notification;
    }
}

Hope this helps.

Upvotes: 2

Related Questions