How to query a web service via POST request in Android?
Asked Answered
S

2

1

I am totally new to Web Feature Service (WFS) but I want to build an Android application with ksoap2-android on top of an API publishing its data via WFS. I would like to request data from the API passing them the bounding box parameter to limit the data that will be returned.

Questions:

  • How can I put the GetFeature object into the SOAP envelope?
  • How can I use JAXBElement on the Android client? See edit from March 15, 2012

Here are some links to the API that might help to understand their format.

Example: WFS-1.1 GetFeature POST request, http://data.wien.gv.at/daten/geoserver/wfs

<?xml version="1.0" encoding="UTF-8"?>
<wfs:GetFeature service="WFS" version="1.1.0"
  outputFormat="JSON"
  xmlns:ogdwien="http://www.wien.gv.at/ogdwien"
  xmlns:wfs="http://www.opengis.net/wfs"
  xmlns:ogc="http://www.opengis.net/ogc"
  xmlns:gml="http://www.opengis.net/gml"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.opengis.net/wfs
  http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" >
 <wfs:Query typeName="ogdwien:BAUMOGD">
   <ogc:Filter>
   <ogc:BBOX>
   <ogc:PropertyName>SHAPE</ogc:PropertyName>
   <gml:Envelope srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">
     <gml:lowerCorner>16.3739 48.2195</gml:lowerCorner>
     <gml:upperCorner>16.3759 48.2203</gml:upperCorner>
   </gml:Envelope>
   </ogc:BBOX>
   </ogc:Filter>
  </wfs:Query>
</wfs:GetFeature>

This is the Android code I came up with by now. This is mostly inspirated by examples from the ksoap2-android wiki. I am totally unsure whether the namespace, methodName and url are correct!

// KSOAP2Client.java

private class MyAsyncTask extends AsyncTask<Void, Void, Object> {
    String namespace = "http://www.wien.gv.at/ogdwien";
    String methodName = "GetFeature";
    String url = "http://data.wien.gv.at/daten/geoserver/wfs";

    protected Object doInBackground(Void... voids) {
        SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
        envelope.dotNet = false;
        SoapObject soapObject = new SoapObject(namespace, methodName);
        envelope.setOutputSoapObject(soapObject);

        // TODO Put request parameters in the envelope. But how?

        try {
            HttpTransportSE httpTransportSE = new HttpTransportSE(url);
            httpTransportSE.debug = true;
            httpTransportSE.call(namespace + methodName, envelope);
            return (Object)soapSerializationEnvelope.getResponse();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return null;
    }
}

EDIT: March 15, 2012


I managed to get further and I almost reached what seems to be the solution. I found the schema definitions for those namespaces used in the XML request and linked them to my project. That allows me to assemble the objects for the request.

// TODO The core libraries won't work with Android.
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;

// TODO Not sure if the versions fit with the service.
import net.opengis.filter.v_1_1_0.BBOXType;
import net.opengis.filter.v_1_1_0.FilterType;
import net.opengis.filter.v_1_1_0.PropertyNameType;
import net.opengis.gml.v_3_1_1.DirectPositionType;
import net.opengis.gml.v_3_1_1.EnvelopeType;
import net.opengis.wfs.v_1_1_0.GetFeatureType;
import net.opengis.wfs.v_1_1_0.QueryType;

[...]

List<Double> lowerCornerList = new Vector<Double>();
lowerCornerList.add(16.3739);
lowerCornerList.add(48.2195);

List<Double> upperCornerList = new Vector<Double>();
upperCornerList.add(16.3759);
upperCornerList.add(48.2203);

DirectPositionType lowerCornerDirectPositionType = new DirectPositionType();
lowerCornerDirectPositionType.setValue(lowerCornerList);
DirectPositionType upperCornerDirectPositionType = new DirectPositionType();
upperCornerDirectPositionType.setValue(upperCornerList);

EnvelopeType envelopeType = new EnvelopeType();
envelopeType.setSrsName("http://www.opengis.net/gml/srs/epsg.xml#4326");
envelopeType.setLowerCorner(lowerCornerDirectPositionType);
envelopeType.setUpperCorner(upperCornerDirectPositionType);

List<Object> propertyNames = new Vector<Object>();
propertyNames.add(new String("SHAPE"));

PropertyNameType propertyNameType = new PropertyNameType();
propertyNameType.setContent(propertyNames);

// TODO Check parameters of JAXBElement.
JAXBElement<EnvelopeType> e = new JAXBElement<EnvelopeType>(null, null, envelopeType);
BBOXType bboxType = new BBOXType();
bboxType.setPropertyName(propertyNameType);
bboxType.setEnvelope(e);

// TODO Check parameters of JAXBElement.
JAXBElement<BBOXType> spatialOps = new JAXBElement<BBOXType>(null, null, bboxType);
FilterType filterType = new FilterType();
filterType.setSpatialOps(spatialOps);

QueryType queryType = new QueryType();
List<QName> typeNames = new Vector<QName>();
// TODO Check parameters of QName.
typeNames.add(new QName("ogdwien", "BAUMOGD"));
queryType.setTypeName(typeNames);

GetFeatureType featureType = new GetFeatureType();
featureType.setService("WFS");
featureType.setVersion("1.1.0");
featureType.setOutputFormat("JSON");
featureType.setMaxFeatures(new BigInteger("5"));

String namespace = "http://www.wien.gv.at/ogdwien";
String methodName = "GetFeature";
// TODO Is this the correct action?
String action = "http://data.wien.gv.at/daten/wfs?service=WFS&request=GetFeature&version=1.1.0&typeName=ogdwien:BAUMOGD&srsName=EPSG:4326";
String url = "http://data.wien.gv.at/daten/geoserver/wfs";

// TODO Is this the correct way to add GetFeature?
SoapObject soapObject = new SoapObject(namespace, methodName);
PropertyInfo propertyInfo = new PropertyInfo();
propertyInfo.setName("GetFeature");
propertyInfo.setValue(featureType);
soapObject.addProperty(propertyInfo);

Still there are a major problem and some minor problems left. The major problem is that JAXBElement is contained in a core library (javax.xml.bind.JAXBElement) that Android refuses to consume. The minor problems are stated in the comments and TODOs.

EDIT: April 27, 2012


As I read this post I imagine that something similar might apply to my problem. I haven't tried yet.

EDIT: May, 09, 2012


Here is the error message from Eclipse when you try to compile JAXBElement for Android.

Singleminded answered 11/2, 2012 at 1:39 Comment(2)
JJD: to answer your question from here. It depends whether you're seeing a VerifyError. If so, it might, otherwise, I don't think so.Dematerialize
@TheTerribleSwiftTomato I'm afraid the answer is no. I added the error message to my post. Any other advise?Singleminded
D
2

@JJD i see you left me a msg in here
I have a meeting in a while, but i took a look at your question and would be glad to help as much as possible. I see you have a problem reading the schema definition . this ws definition you have is chained one inside the other thats why it confused you reading it:

If you go on : http://schemas.opengis.net/wfs/1.1.0/wfs.xsd

take the method "GetCapabilities" and let's read it in the Web service definition:

PS:I did not test these but:


Now You have the request of GetCapabilities:

<!-- REQUEST -->
<xsd:element name="GetCapabilities" type="wfs:GetCapabilitiesType"/>
<xsd:complexType name="GetCapabilitiesType">
    <xsd:annotation>
    </xsd:annotation>
    <xsd:complexContent>
    <xsd:extension base="ows:GetCapabilitiesType">
    <xsd:attribute name="service" type="ows:ServiceType" use="optional" default="WFS"/>
    </xsd:extension>
    </xsd:complexContent>
</xsd:complexType>

GetCapabilities has a complex type of the type: GetCapabilitiesType, which you find in one of the xsd link on this page, precisely "owsGetCapabilities.xsd"

--> After opening it ie, : http://schemas.opengis.net/ows/1.0.0/owsGetCapabilities.xsd

You find this complex type definition:

<complexType name="GetCapabilitiesType">
<annotation>
     <documentation>
         XML encoded GetCapabilities operation request. This operation 
         allows clients to retrieve service metadata about a specific service instance.
         In this XML encoding, no "request" parameter is included, since the element name 
         specifies the specific operation. This base type shall be extended by each specific 
         OWS to include the additional required "service" attribute, with the correct value for that OWS. 
     </documentation>
</annotation>
<sequence>
     <element name="AcceptVersions" type="ows:AcceptVersionsType" minOccurs="0">
         <annotation>
             <documentation>When omitted, server shall return latest supported version. 
             </documentation>
         </annotation>
     </element>
     <element name="Sections" type="ows:SectionsType" minOccurs="0">
         <annotation>
             <documentation>
                 When omitted or not supported by server, 
                 server shall return complete service metadata (Capabilities) document. 
             </documentation>
         </annotation>
     </element>
     <element name="AcceptFormats" type="ows:AcceptFormatsType" minOccurs="0">
         <annotation>
             <documentation>
                 When omitted or not supported by server, server shall return service metadata 
                 document using the MIME type "text/xml". 
             </documentation>
         </annotation>
     </element>
 </sequence>
<attribute name="updateSequence" type="ows:UpdateSequenceType" use="optional">
     <annotation>
         <documentation>
             When omitted or not supported by server, 
             server shall return latest complete service 
             metadata document. 
         </documentation>
     </annotation>
</attribute>
</complexType>

Now this GetCapabilitiesType has elements /attributes:
name="AcceptVersions" of type="ows:AcceptVersionsType" and minOccurs="0" ie can be null name="Sections" of type="ows:SectionsType" and minOccurs="0" ie can be null name="AcceptFormats" of type="ows:AcceptFormatsType" and minOccurs="0" ie can be null name="updateSequence" of type="ows:UpdateSequenceType" and its is optional -->use="optional"

Where to find these attributes definitions?
-->on this same page you have: AcceptVersionsType is :

<complexType name="AcceptVersionsType">
 <annotation>
 <documentation>
  Prioritized sequence of one or more specification versions accepted by client, with preferred versions listed first. See Version negotiation subclause for more information.     
 </documentation>
 </annotation>
 <sequence>
  <element name="Version" type="ows:VersionType" maxOccurs="unbounded"/>
 </sequence>
</complexType>


so AcceptVersionsType has an element of type: VersionType can be found at xsd owsOperationsMetadata.xsd ( which is on the same link of this page) and on it you have xsd:owsCommon.xsd this is this where VersionType is found ie: http://schemas.opengis.net/ows/1.0.0/owsCommon.xsd

<simpleType name="VersionType">
  <annotation>
    <documentation>Specification version for OWS operation. The string value shall contain one x.y.z "version" value (e.g., "2.1.3"). A version number shall contain three non-negative integers separated by decimal points, in the form "x.y.z". The integers y and z shall not exceed 99. Each version shall be for the Implementation Specification (document) and the associated XML Schemas to which requested operations will conform. An Implementation Specification version normally specifies XML Schemas against which an XML encoded operation response must conform and should be validated. See Version negotiation subclause for more information. </documentation>
  </annotation>
  <restriction base="string"/>
</simpleType>

and sectionsType is:

<complexType name="SectionsType">
 <annotation>
   <documentation>
    Unordered list of zero or more names of requested sections in complete service metadata document. Each Section value shall contain an allowed section name as specified by each OWS specification. See Sections parameter subclause for more information.            
   </documentation>
 </annotation>
 <sequence>
    <element name="Section" type="string" minOccurs="0" maxOccurs="unbounded"/>
 </sequence>
</complexType>

(SectionsType has an element of simple type String)

And AcceptFormatsType is:

<complexType name="AcceptFormatsType">
 <annotation>
   <documentation>
    Prioritized sequence of zero or more GetCapabilities operation response formats desired by client, with preferred formats listed first. Each response format shall be identified by its MIME type. See AcceptFormats parameter use subclause for more information. 
   </documentation>
 </annotation>
 <sequence>
  <element name="OutputFormat" type="ows:MimeType" minOccurs="0" maxOccurs="unbounded"/>
  </sequence>
</complexType>

(AcceptFormatsType has an element of type MimeType which is found same place as VersionType ie : http://schemas.opengis.net/ows/1.0.0/owsCommon.xsd

<simpleType name="MimeType">
  <annotation>
    <documentation>XML encoded identifier of a standard MIME type, possibly a parameterized MIME type. </documentation>
  </annotation>
  <restriction base="string">
    <pattern value="(application|audio|image|text|video|message|multipart|model)/.+(;s*.+=.+)*"/>
  </restriction>
</simpleType>

and UpdateSequenceType is( it is a simple type not complex type):

 <simpleType name="UpdateSequenceType">
 <annotation>
  <documentation>
   Service metadata document version, having values that are "increased" whenever any change is made in service metadata document. Values are selected by each server, and are always opaque to clients. See updateSequence parameter use subclause for more information.     
   </documentation>
  </annotation>
 <restriction base="string"/>
 </simpleType>

(UpdateSequenceType is a simple type)

Now i hope it got clearer how to read the schema . Now complex type means object unlike simple type (ex: int). When you have complex types, and you are using ksoap2, you have to create local representations in classes (objects) that implements kvmSerializable ( a ksoap2 serialization interface).

Now , you can read my answers to know how to do that on : Link1,link2,link3. I wrote some details which will help you understand how to start coding.

I won't be on my pc for the day. Hope this helps,let me know if anything of wt i said is ambigous.

Drayman answered 12/2, 2012 at 16:37 Comment(5)
Wow! Thx! Does that mean I have to add a class for each complex type listed in the specification until I hit a simple type like string in order to communicate with the web service? That means I have to copy their whole interface. - Is there no other way? I imagine there is an opensource SOAP client that reads the definitions on runtime... If they change anything my client will fail immediately.Singleminded
I wonder where do I use their domain name: data.wien.gv.at - Can you please add to your post what you would specify for namespace, methodName, soapAction, url in case of the ksoap2-android client?Singleminded
@Singleminded a complex type is an object. which means, on the web service, there is a class (object) that this complex type is pointing to. However in ksoap2, ur building the enveloppe manually, so you have to create a local class (that implements kvmserializable) that represent the object on the Web service. Also you have to addMapping for the server to know when getting the soap request that complex type X maps to class X.(all these are explained in the links i gave u ). i dunno if there is an Open source Soap client, it would be nice. Namespace,url & others are mentioned in my answer(not tested)Drayman
I understand the concept now. I just thought it would be less complex to implement the client. Don't you agree that such a fixed interface/implementation is hard to maintain.. For the namespace I think it has to be something about data.wien.gv.at not the url of the specification.Singleminded
@Singleminded good stuff, keep up the good work! yeah when you are used to using for example netbeans to call a web service, and u try to use ksoap2, at first it is hard to understand and feels complex. But don't worry u'll get used to it. If you need any other help let me know, meanwhile if my answer was what you are looking for "accept" it so that it can be used by other users with the same problem. thxDrayman
T
0

Normally, and from what little experience I have with ksoap2, you would do something like this.

SoapObject request = new SoapObject("http://www.webserviceX.NET", "GetCitiesByCountry");
String soapAction = "http://www.webserviceX.NET/GetCitiesByCountry";

request.addProperty("CountryName", "india");
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.bodyOut = request;
envelope.dotNet = true;

HttpTransport ht = new HttpTransport("http://www.webservicex.net/globalweather.asmx");
ht.debug = true;
//System.err.println( ht.requestDump );

ht.call(soapAction,envelope);
System.out.println("####################: " +envelope.getResponse());
//SoapObject result = (SoapObject)envelope.getResponse();

So basically you should just take your soapObject and call addProperty() to it.

Tillie answered 11/2, 2012 at 20:42 Comment(4)
How would you add properties in my case? I need to specify the bounding box parameter for the request. I also unsure if the namespace, .. are correct?!Singleminded
The namespace has to be written exactly and is case sensitive. Eg. webserviceX.NET is different from webserviceX.net This is one of the primarily reasons things with ksoap2 go wrongTillie
As for the stuff around the SoapEnvelope, hmm I am not quite sure actually, but wouldn't it be about the same way, but you just have to specify it as a property or template to the envelope?Tillie
I am still not able to read the right scheme from the xml. Could you please adapt your example to my problem (data.wien.gv.at)?Singleminded

© 2022 - 2024 — McMap. All rights reserved.