Enterprise J2ME Developing Mobile Java Applications [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Enterprise J2ME Developing Mobile Java Applications [Electronic resources] - نسخه متنی

Michael Juntao Yuan

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید



16.3 kSOAP Explained


In this section, we look under the hood to understand exactly how kSOAP v1.2 works. We also learn how to customize kSOAP for our special needs. For the impatient readers, a summary of the kSOAP API is available in Table 16.2 in Section 16.3.5. Now, let's start by examining how kSOAP maps SOAP data structures to Java objects.


16.3.1 The Default Mapping


By default, kSOAP understands the mappings described in Table 16.1. Notice that xsd:float is not supported due to the lack of Float support in CLDC v1.0.

Table 16.1. Default Type Mapping in kSOAP

SOAP type

Java type

xsd:int

java.lang.Integer

xsd:long

java.lang.Long

xsd:string

java.lang.String

xsd:Boolean

java.lang.Boolean

When a simple kSOAP parser encounters a SOAP element, the parser reads the XML element into a Java object according to the following rules:

If the SOAP element is one of the default primitive types in the table above, it converts to a Java object of a matching type.

If the SOAP element has no children (a primitive element) but is not a default type, it converts to a SoapPrimitive object. We can retrieve the element's original SOAP type information from the SoapPrimitive.getNamespace() and SoapPrimitive.getName() methods. We can access the element's string value from the SoapPrimitive.toString() method.

If the SOAP element has children (a complex element), it converts to a KvmSerializable object. KvmSerializable is an interface; the kSOAP package provides the interface's convenience implementation: SoapObject. Similar to SoapPrimitive objects, you can retrieve the element's original SOAP type information from the SoapObject.getNamespace() and SoapObject.getName() methods.

A complex element's children convert to properties inside the parent SoapObject according to the above three rules in this list. Each property also has an associated PropertyInfo object containing information such as the SOAP element name and the property's Java object type. The PropertyInfo class supports element namespace in kSOAP v2.0 beta.



16.3.2 Object Structure


Since a SoapObject can take other SoapObjects as properties, we can use SoapObject to express complex hierarchical structures. An analogy to the XML DOM can be drawn here: The SoapObject resembles the parent node, and the property and PropertyInfo pairs resemble child nodes.

In simple cases, like the Google spell check example where the document contains only one primitive SOAP element, we can directly retrieve the mapped Java object or SoapPrimitive object through the SoapEnvelope.getBody() or SoapEnvelope.getResult() methods. If the SOAP message contains more information, the above two get methods return a root SoapObject, which contains the entire hierarchy of Java objects mapped from the SOAP elements according to the above rules.


getBody() versus getResult()


The getBody() method gets the first child element of the SOAP-ENV:Body element. The getResult() method gets the first grandchild of the body element. Method getResult() is used in HttpTransport class to parse the RPC response message because the return value is always embedded in a result element under the SOAP body element.

Figure 16.2 shows such a structure. Each node on the tree represents a SOAP element in the parsed document. The original SOAP element name is stored in the PropertyInfo object, and the SOAP type attribute name/namespace is stored in the property object. Arrows represent property relationships. You can find the Java data objects' original SOAP type information by searching the mappings in the ClassMap object, which contains the information in the above table (more on ClassMap later). As we can see, the root SoapObject lacks a pairing PropertyInfo. Therefore, we lose the root element's name after parsing. However, that is not an issue when we read SOAP documents, since element names normally serve only as indexes for accessing Java data objects, and we do not need an index to access the root element.


Figure 16.2. Structure of a parsed SOAP document.



16.3.3 Custom Mapping Through Data Marshal


To further automate the marshaling process to handle custom-defined types, we must prepare the parser for two tasks:

The parser must know the mapping relationship between custom SOAP types to custom Java types. We complete custom mapping by adding matching type pairs to the current parser's ClassMap object.

Since all SOAP data are presented in plain text strings, the parser must know how to convert the string to a desired Java object. The parser converts the string through a Marshal object, which is registered with the parser's corresponding custom SOAP and Java type pair in the ClassMap object.



SOAP Writing


To compose a SOAP message, we reverse the process. We first build the SoapObject hierarchy in memory. All leaf properties must be either SoapPrimitive or one of the four default type Java objects. Then, we use a kSOAP writer object to serialize the memory object to an XML stream. However, as you might already see, the root element presents a serialization problem. When we construct the structure according to the figure, the root SoapObject contains only the SOAP type name and namespace. No XML element name is available for the root element due to the pairing PropertyInfo's absence. We cannot write an XML element without an element name. The kSOAP v1.2 writer sidesteps this problem by making a notable exception to the parsing rule: When we serialize a root SoapObject, its name and namespace are used as element name/namespace rather than the SOAP type name/namespace (see the Google spell check example).

kSOAP provides a standard way to add custom type mapping and marshaling capabilities through the implementation of interface Marshal. The source code for the Marshal interface is shown in Listing 16.5. It declares a method to register itself with ClassMap and two call back methods.

Listing 16.5. The Marshal interface


public interface Marshal {
public Object readInstance (SoapParser parser,
String namespace, String name,
ElementType expected) throws IOException;
public void writeInstance (SoapWriter writer,
Object instance) throws IOException;
public void register (ClassMap cm);
}

The kSOAP download package provides three Marshal implementations as both convenience tools and programming examples. Let's look at the MarshalDate class, which marshals xsd:dateTime type elements, for an example. Method Marshal.readInstance() actually reads the text string from the SOAP element and then converts it to a Java object. In the case of the MarshalDate,readInstance()'s source code is shown in Listing 16.6

Listing 16.6. The readInstance() method in the MarshalDate class


public Object readInstance (SoapParser parser,
String namespace, String name,
ElementType expected) throws IOException {
parser.parser.read (); // Start tag
Object result =
IsoDate.stringToDate (parser.parser.readText (), IsoDate.DATE_TIME);
parser.parser.read (); // End tag
return result;
}

Similarly, method Marshal.writeInstance() describes how to serialize the Java object to a SOAP text string. In kSOAP writers, the method offers a more elegant alternative to using SoapPrimitive objects to handle unknown types. Interested readers can find the source code for that function from kSOAP source distribution. Method Marshal.register() (Listing 16.7) adds the matching custom SOAP type and Java type pair, as well as their processing Marshal object, to a ClassMap object.

Listing 16.7. The register() method for MarshalDate


public void register (ClassMap cm) {
cm.addMapping (cm.xsd, "dateTime", MarshalDate.DATE_CLASS, this);
}

Now, let's look at a more complex example that demonstrates the use of SoapObject and Marshal.


16.3.4 A More Complex Example


The example scenario is a response from a stock trade SOAP service. The message is shown in Listing 16.8.

Listing 16.8. Demo stock trade response message



<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://www.w3.org/2001/12/soap-envelope"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:n="http://www.javaworld.com/ksoap/test">
<SOAP-ENV:Body>
<result>
<OrderStatus xsi:type="n:orderStatus">
<CustomerName xsi:type="xsd:string">
Michael Yuan
</CustomerName>
<Transaction xsi:type="n:transaction">
<Symbol xsi:type="xsd:string">XYZ</Symbol>
<Share xsi:type="xsd:int">1000</Share>
<Buy xsi:type="xsd:boolean">true</Buy>
<Price xsi:type="xsd:float">123.45</Price>
</Transaction>
<ExecTime xsi:type="xsd:dateTime">
2003-03-05T23:20:50.52Z
</ExecTime>
</OrderStatus>
</result>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Listing 16.9 illustrates how to parse and retrieve information from the complex structure. Notice that the Price element is wrapped in a SoapPrimitive because xsd:float is not supported; the ExecTime element is correctly marshaled into a Java Date object because of the use of the MarshalDate.

Listing 16.9. Parsing the stock trade response



// Feed the response message into a
// reader object
XmlParser xp = new XmlParser (reader);
// Register Marshal for "xsd:dateTime" type
ClassMap cm = new ClassMap (Soap.VER12);
Marshal md = new MarshalDate ();
md.register (cm);
SoapEnvelope envelope = new SoapEnvelope (cm);
envelope.parse (xp);
// Get the parsed structure
SoapObject orderStatus = (SoapObject) envelope.getResult();
String customerName = (String)orderStatus.getProperty("CustomerName");
// Second layer of SoapObject
SoapObject transaction =
(SoapObject)orderStatus.getProperty("Transaction");
String symbol = (String) transaction.getProperty ("Symbol");
Integer share = (Integer) transaction.getProperty ("Share");
Boolean buy = (Boolean) transaction.getProperty ("Buy");
SoapPrimitive price = (SoapPrimitive)transaction.getProperty("Price");
Date execTime = (Date) orderStatus.getProperty ("ExecTime");

In addition to the MarshalDate class, kSOAP provides two other Marshal implementations: MarshalBase64 and MarshalHashtable. Base64 is a method for encoding a binary stream into an ASCII string so that it can transport through email or XML/SOAP. MarshalBase64 marshals an xsd:based64Binary element into a Java byte array. kSOAP does not support SOAP attachments, but MarshalBase64 should allow users to send/receive binary data chunks. MarshalHashtable marshals a SOAP Map element into a Java hashtable.


16.3.5 Recap: The kSOAP API


We have covered almost all public classes and interfaces in the org.ksoap and org.kobjects.serialization packages. In recap, the classes and their descriptions are shown in Table 16.2.


/ 204