Simple framework skip soap envelope and body

二次信任 提交于 2019-12-30 18:55:08

问题


I'm using RetroFit and Simple XML Framework in Android to model a SOAP response that looks like this:

XML:

<soap:Envelope 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
    <BuslocationResponse 
        xmlns="AT_WEB">
        <Version>1.0</Version>
        <Responsecode>0</Responsecode>
        <Input>
            <Route>801</Route>
            <Direction>N</Direction>
        </Input>
        <Vehicles>
            <Vehicle>
                <Route>801</Route>
                <Direction>N</Direction>
                <Updatetime>09:42 PM</Updatetime>
                <Vehicleid>5007</Vehicleid>
                <Block>801-06</Block>
                <Adherance>-2</Adherance>
                <Adhchange>S</Adhchange>
                <Reliable>Y</Reliable>
                <Offroute>N</Offroute>
                <Stopped>N</Stopped>
                <Inservice>Y</Inservice>
                <Speed>20.61</Speed>
                <Heading> 3</Heading>
                <Routeid>44916</Routeid>
                <Positions>
                    <Position>30.221222,-97.765007</Position>
                    <Position>30.218363,-97.766747</Position>
                    <Position>30.215282,-97.768715</Position>
                    <Position>30.212505,-97.770485</Position>
                    <Position>30.204943,-97.774765</Position>
                    <Position>30.204035,-97.775078</Position>
                </Positions>
            </Vehicle>
        </Vehicles>
</BuslocationResponse>
</soap:Body>
</soap:Envelope>

Really, all I care about is the collection of vehicles. It seems like I could model just the BusLocationResponse and skip the soap envelope and body by declaring the

Java:

@Root(strict=false)
@Path("Envelope/Body/BuslocationResponse")
public class BusLocationResponse {

    @Element(name="Responsecode")
    public int responseCode;

    @ElementList
    @Path("Envelope/Body/BuslocationResponse/Vehicles")
    public List<CapVehicle> vehicles;

}

This just yields the error:

org.simpleframework.xml.core.ValueRequiredException: Unable to satisfy
@org.simpleframework.xml.Element(data=false, name=Responsecode, required=true,
type=void) on field 'responseCode' 

What am I misunderstanding here?


回答1:


You can't use @Path on @Root-Element:

The Path annotation is used to specify an XML path where an XML element or attribute is located.

( Source )

Since you want nested data, from somewhere deep in the xml, there are two solutions:

  1. Map the whole XML structure
  2. Use a Converter that cut's mapping down to few classes and map just those

And here's what to do if you choose No. 2:

The Plan

  • A SOAPEnvelope class builds just the root-element (<soap:Envelope>...</soap:Envelope>) and holds the list of vehicles
  • A SOAPEnvelopeConverter implements a Converter for SOAPEnvelope - there the serialization is reduced to vehicles list only
  • A class Vehicle holds all the data of those elements (incl. a class Position for the <Position>...</Position> elements)
  • A class Vehicles maps the vehicles-tag only (= list of vehicle elements).

(Names have no convention)

The Implementation

I've written an implementation as a reference so you can see how my suggested solution works. Please add error checking etc. All data fields are handled as String's here, replace their types with proper ones. Only the vehicles list is deserialized, all other values are ignored. Constructors, getter / setter etc. are only shown as they are required for this example.

The deserialized vehicles list is stored into the envelope's object. This is not the best way and used for example only. Please write a better implementation here (eg. introduce a class for the soap body where you can manage contents).

Note: Some classes are implemented as inner classes - this is optional, code as you prefer.

Class SOAPEnvelope / Class SOAPEnvelopeConverter (inner)

@Root(name = "Envelope")
@Namespace(prefix = "soap")
// Set the converter that's used for serialization
@Convert(value = SOAPEnvelope.SOAPEnvelopeConverter.class)
public class SOAPEnvelope
{
    // Keep the content of vehicles list here
    private Vehicles vehicles;


    public Vehicles getVehicles()
    {
        return vehicles;
    }

    protected void setVehicles(Vehicles vehicles)
    {
        this.vehicles = vehicles;
    }



    // The converter implementation for SOAPEnvelope
    public static class SOAPEnvelopeConverter implements Converter<SOAPEnvelope>
    {
        @Override
        public SOAPEnvelope read(InputNode node) throws Exception
        {
            SOAPEnvelope envelope = new SOAPEnvelope();
            InputNode vehiclesNode = findVehiclesNode(node); // Search the Vehicles list element

            if( vehiclesNode == null )
            {
                // This is bad - do something useful here
                throw new Exception("No vehicles node!");
            }

            /*
             * A default serializer is used to deserialize the full node. The
             * returned object is set into the envelops's object, where you can
             * get it through a get()-method.
             */
            Serializer ser = new Persister();
            envelope.setVehicles(ser.read(Vehicles.class, vehiclesNode));

            return envelope;
        }


        @Override
        public void write(OutputNode node, SOAPEnvelope value) throws Exception
        {
            // If you read (deserialize) only there's no need to implement this
            throw new UnsupportedOperationException("Not supported yet.");
        }


        private InputNode findVehiclesNode(InputNode rootNode) throws Exception
        {
            InputNode body = rootNode.getNext("Body");
            InputNode buslocationResponse = body.getNext("BuslocationResponse");

            InputNode next;

            while( ( next = buslocationResponse.getNext() ) != null )
            {
                if( next.getName().equals("Vehicles") == true )
                {
                    return next;
                }
            }

            return null;
        }
    }
}

Class Vehicles

@Root(name = "Vehicles")
public class Vehicles
{
    // Maps the list of vehicles
    @ElementList(name = "Vehicles", inline = true)
    private List<Vehicle> vehicles;
}

Class Vehicle

@Root(name = "Vehicle")
public class Vehicle
{
    // All values are of type String - please replace with proper types
    @Element(name = "Route")
    private String route;
    @Element(name = "Direction")
    private String direction;
    @Element(name = "Updatetime")
    private String updateTime;
    @Element(name = "Vehicleid")
    private String vehicleID;
    @Element(name = "Block")
    private String block;
    @Element(name = "Adherance")
    private String adherance;
    @Element(name = "Adhchange")
    private String adhchange;
    @Element(name = "Reliable")
    private String reliable;
    @Element(name = "Offroute")
    private String offroute;
    @Element(name = "Stopped")
    private String stopped;
    @Element(name = "Inservice")
    private String inservice;
    @Element(name = "Speed")
    private String speed;
    @Element(name = "Heading")
    private String heading;
    @Element(name = "Routeid")
    private String routeID;
    @ElementList(name = "Positions")
    private List<Position> postions;


    // A class to map the position elements
    @Root(name = "Position")
    public static class Position
    {
        @Text()
        private String position;
    }
}

How to use

final String xml = ...
Serializer ser = new Persister(new AnnotationStrategy()); // Annotation strategy is set here!

SOAPEnvelope soapEnvelope = ser.read(SOAPEnvelope.class, new StringReader(xml));

Nothing special here - only AnnotationStrategy is required! The source (2nd parameter of ser.read() is set as your input comes. In this example, the soap xml comes from a string.




回答2:


the simple way is use @path , example i want get Route from node Soap/Body/BuslocationResponse/Vehicles/Vehicle

Response.java

@Root(name = "soap:Envelope", strict = false)
public class Response {
   @Element(name = "Route")
   @Path("Body/BuslocationResponse/Vehicles/Vehicle")
   private int route;

   public int getRoute() {
    return route;
   }
}

Main.java

class Main {
public static void main(String args[]) {
    String xml = "<soap:Envelope \n" +
            "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
            "xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"\n" +
            "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n" +
            "soap:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"\n" +
            "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
            "<soap:Body>\n" +
            "    <BuslocationResponse \n" +
            "        xmlns=\"AT_WEB\">\n" +
            "        <Version>1.0</Version>\n" +
            "        <Responsecode>0</Responsecode>\n" +
            "        <Input>\n" +
            "            <Route>801</Route>\n" +
            "            <Direction>N</Direction>\n" +
            "        </Input>\n" +
            "        <Vehicles>\n" +
            "            <Vehicle>\n" +
            "                <Route>801</Route>\n" +
            "                <Direction>N</Direction>\n" +
            "                <Updatetime>09:42 PM</Updatetime>\n" +
            "                <Vehicleid>5007</Vehicleid>\n" +
            "                <Block>801-06</Block>\n" +
            "                <Adherance>-2</Adherance>\n" +
            "                <Adhchange>S</Adhchange>\n" +
            "                <Reliable>Y</Reliable>\n" +
            "                <Offroute>N</Offroute>\n" +
            "                <Stopped>N</Stopped>\n" +
            "                <Inservice>Y</Inservice>\n" +
            "                <Speed>20.61</Speed>\n" +
            "                <Heading> 3</Heading>\n" +
            "                <Routeid>44916</Routeid>\n" +
            "                <Positions>\n" +
            "                    <Position>30.221222,-97.765007</Position>\n" +
            "                    <Position>30.218363,-97.766747</Position>\n" +
            "                    <Position>30.215282,-97.768715</Position>\n" +
            "                    <Position>30.212505,-97.770485</Position>\n" +
            "                    <Position>30.204943,-97.774765</Position>\n" +
            "                    <Position>30.204035,-97.775078</Position>\n" +
            "                </Positions>\n" +
            "            </Vehicle>\n" +
            "        </Vehicles>\n" +
            "</BuslocationResponse>\n" +
            "</soap:Body>\n" +
            "</soap:Envelope>";

    try {
        Serializer serializer = new Persister();
        Response r = serializer.read(Response.class, xml);
        System.out.println("route: " + r.getRoute());
    } catch (Exception e) {
        e.printStackTrace();
    }
}
} 

Result:

/usr/lib/jvm/java-8-oracle/bin/java....
route: 801
Process finished with exit code 0


来源:https://stackoverflow.com/questions/24102741/simple-framework-skip-soap-envelope-and-body

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!