Search code examples
javaxmlinheritancesimple-framework

How to map data to the right inheriting class with Framework Simple


how can I map the subsequent XML file to the right inheriting class. The problem is, that I have four different API methods, this is a response of one of the methods named "api_search_location"

<?xml version="1.0" encoding="UTF-8"?>
<ft>
  <response>
    <client device="" appName="" clientId="123" appVersion=""/>
    <responseType>api_search_location</responseType>
    <responseTime>2013-01-21 11:55:43</responseTime>
    <locations status="list">
      <location name="60201040" title="Praterstern" municipality="Wien" type="stop" listIndex="0:1" coordName="wgs84" wgs84Lat="48.21815" wgs84Lon="16.39176" coordX="" coordY="" distanceMeter="" durationMinutes=""/>
      <location name="60201848" title="Praterstern/Lassallestraße" municipality="Wien" type="stop" listIndex="1:2" coordName="wgs84" wgs84Lat="48.21962" wgs84Lon="16.39382" coordX="" coordY="" distanceMeter="" durationMinutes=""/>
    </locations>
    <message messageCode="1">ok</message>
  </response>
</ft>

I have the following class "FtResponse" for all the four response types:

package model.response;

import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;

@Root(name="ft")
public class FtResponse {
    @Element
    private Response response;

    public FtResponse() {
        super();
    }

    public FtResponse(Response response) {
        super();
        this.response = response;
    }

    public Response getResponse() {
        return response;
    }

    public void setResponse(Response response) {
        this.response = response;
    }

    @Override
    public String toString() {
        return "FtResponse [response=" + response + "]";
    }

}

And I have the class "Response". All the specific response classes inherit from this general class:

package model.response;

import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;

    @Root(name = "request")
    public class Response {
        @Element
        private Client client;
        @Element
        private String responseType;
        @Element
        private String responseTime;

        public Response() {
            super();
        }

        public Response(Client client, String responseType, String responseTime) {
            super();
            this.client = client;
            this.responseType = responseType;
            this.responseTime = responseTime;
        }

        public Client getClient() {
            return client;
        }

        public void setClient(Client client) {
            this.client = client;
        }

        public String getResponseType() {
            return responseType;
        }

        public void setResponseType(String responseType) {
            this.responseType = responseType;
        }

        public String getResponseTime() {
            return responseTime;
        }

        public void setResponseTime(String responseTime) {
            this.responseTime = responseTime;
        }

        @Override
        public String toString() {
            return "Response [client=" + client + ", responseType=" + responseType
                    + ", responseTime=" + responseTime + "]";
        }

    }

This is one of my specific response classes:

package model.response;

import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;

@Root(name = "response")
public class SearchLocationStopsNearbyResponse extends Response {
    @Element
    private Client client;
    @Element
    private String responseType;
    @Element
    private String responseTime;
    @Element
    private LocationCentre locationCentre;
    @Element
    private Locations locations;
    @Element
    private Message message;

    public SearchLocationStopsNearbyResponse() {
        super();
    }

    public SearchLocationStopsNearbyResponse(Client client,
            String responseType, String responseTime,
            LocationCentre locationCentre, Locations locations, Message message) {
        super();
        this.client = client;
        this.responseType = responseType;
        this.responseTime = responseTime;
        this.locationCentre = locationCentre;
        this.locations = locations;
        this.message = message;
    }

    public Client getClient() {
        return client;
    }

    public void setClient(Client client) {
        this.client = client;
    }

    public String getResponseType() {
        return responseType;
    }

    public void setResponseType(String responseType) {
        this.responseType = responseType;
    }

    public String getResponseTime() {
        return responseTime;
    }

    public void setResponseTime(String responseTime) {
        this.responseTime = responseTime;
    }

    public LocationCentre getLocationCentre() {
        return locationCentre;
    }

    public void setLocationCentre(LocationCentre locationCentre) {
        this.locationCentre = locationCentre;
    }

    public Locations getLocations() {
        return locations;
    }

    public void setLocations(Locations locations) {
        this.locations = locations;
    }

    public Message getMessage() {
        return message;
    }

    public void setMessage(Message message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return "SearchLocationStopsNearbyResponse [client=" + client
                + ", responseType=" + responseType + ", responseTime="
                + responseTime + ", locationCentre=" + locationCentre
                + ", locations=" + locations + ", message=" + message + "]";
    }

}

My problem is, that when i try to read in all the data to my nested classes i get the following exception:

org.simpleframework.xml.core.ElementException: Element 'locationCentre' does not have a match in class model.response.Response at line 7
    at org.simpleframework.xml.core.Composite.readElement(Composite.java:527)
    at org.simpleframework.xml.core.Composite.readElements(Composite.java:445)
    at org.simpleframework.xml.core.Composite.access$400(Composite.java:59)
    at org.simpleframework.xml.core.Composite$Builder.read(Composite.java:1383)
    at org.simpleframework.xml.core.Composite.read(Composite.java:201)
    at org.simpleframework.xml.core.Composite.read(Composite.java:148)
    at org.simpleframework.xml.core.Composite.readVariable(Composite.java:623)
    at org.simpleframework.xml.core.Composite.readInstance(Composite.java:573)
    at org.simpleframework.xml.core.Composite.readUnion(Composite.java:549)
    at org.simpleframework.xml.core.Composite.readElement(Composite.java:532)
    at org.simpleframework.xml.core.Composite.readElements(Composite.java:445)
    at org.simpleframework.xml.core.Composite.access$400(Composite.java:59)
    at org.simpleframework.xml.core.Composite$Builder.read(Composite.java:1383)
    at org.simpleframework.xml.core.Composite.read(Composite.java:201)
    at org.simpleframework.xml.core.Composite.read(Composite.java:148)
    at org.simpleframework.xml.core.Traverser.read(Traverser.java:92)
    at org.simpleframework.xml.core.Persister.read(Persister.java:625)
    at org.simpleframework.xml.core.Persister.read(Persister.java:606)
    at org.simpleframework.xml.core.Persister.read(Persister.java:584)
    at org.simpleframework.xml.core.Persister.read(Persister.java:543)
    at org.simpleframework.xml.core.Persister.read(Persister.java:521)
    at org.simpleframework.xml.core.Persister.read(Persister.java:426)
    at TestRead.main(TestRead.java:19)

I unterstood that Simple tries to find the element "locationCentre" in the class "Response", but there is no appropriate element. But how can I say to Simple, that it should choose the correct specific class, in this case "SearchLocationStopsNearbyResponse" depending on the XML response file?


Solution

  • If i understand aright, you use <responseType>...</responseType> to decide which implementation of Response is the correct one. I guess there's no such functionality in Simple.

    Possibly unions can help you here.

    However - you can customize the process of serialization / deserialisation by using a Converter or Visitor. This may not as easy as some annotations but should be capable of solving your problem.

    So here is what you have to do if you use a Converter:

    • Implement a new class eg. ResponseConverter which implements the Converter-Interface (if you dont need it you can leave the write()-method)
    • Mark your Response-class (and the classes wich extend it?) with @Convert annotation (eg. @Convert(ResponseConverter)) so simple knows that you want to use your own.
    • Add the AnnotationStrategy to your Serializer (eg. Serializer ser = new Persister(new AnnotationStrategy());)

    See also: