• No results found

SOA for S60 Platform XML API

Chapter 7: Enabling Mobile Web Services

7.7 SOA for S60 Platform XML Processing

7.7.2 SOA for S60 Platform XML API

As noted earlier, there are different ways to parse an XML document, and a software application has several different needs concerning an XML processor:

• Parsing into an appropriate structure from an XML document

• Ability to locate one or more elements from an XML document that the application is interested in

• Ability to provide classes that allow the hiding of the XML nature of an application object (such as the Person object shown above in Example 7-16)

The S60 platform provides general XML processing features of this nature. They support a full DOM and SAX parsing approach, and allow software developers to create their own XML node trees able to serialize themselves as XML. The features are based on a well-known libxml2 parser (for more information, visit <xmlsoft.org>).

In addition to the platform’s parsing capabilities, the SOA for S60 platform API itself provides some further XML processing APIs. They are intended to simplify some of the aspects of developing XML-based software. The APIs are described in the following sections.

7.7.2.1

7.7.2.1 CSenBaseElement CSenBaseElement

The CSenBaseElement class (and descendents) can be used in a DOM-like fashion to deal with a node tree. This is particularly useful when creating a set (node tree) of objects to be serialized as an XML message.

The following example is taken from a service consumer application for the eBay auction Web services. (This example deliberately omits the typical Symbian memory management code (CleanupStack::

PushL() and Pop() ) for clarity.):

CSenElement* secHeader =

CSenBaseElement::NewL(_L8(”RequesterCredentials”));

secHeader->SetNamespace(KNsUri);

secHeader->AddAttrL(_L8(”S:mustUnderstand”), _L8(”0”));

//eBayAuthToken

CSenElement* element = CSenBaseElement::NewL(_L8(”eBayAuthToken”));

// set the content of the containing element // to be the auth token string

element->SetContentL(KeBayAuthToken);

// add the element to the parent element (ie.

// as a child node of the tree) secHeader->AddElementL( *element );

//Credentials

element = CSenBaseElement::NewL(_L8(”Credentials”));

element->SetNamespace(KNsPrefix, KNsUri);

//DevId

CSenElement* childEl = CSenBaseElement::NewL(_L8(”DevId”));

childEl->SetContentL(KDevId);

// add a child node to the Credentials element element->AddElementL( *childEl );

//DevId 2

childEl = CSenBaseElement::NewL(_L8(”AppId”));

childEl->SetContentL(KAppId);

// add another child node to the Credentials element

element->AddElementL( *childEl );

//DevId 3

childEl = CSenBaseElement::NewL(_L8(”AuthCert”));

childEl->SetContentL(KAuthCert);

// add another child node to the Credentials element element->AddElementL( *childEl );

//Add Credentials element as a child of RequesterCredentials secHeader->AddElementL( *element );

Example 7-17. Creating credentials for eBay.

The resulting object tree based on XML elements could then be serialized as an XML text fragment (for example, by calling

secHeader->AsXmlL()).

7.7.2.2

7.7.2.2 CSenDomFragment CSenDomFragment

The CSenDomFragment class is useful for parsing a message where you know you are probably interested in the entire structure of the XML message being parsed, but do not necessarily know the name of the top-level element of the incoming message. Thus, you might not be able to register a SAX handler for the unknown element. This approach is most useful when you know that the XML representation of the data is practically the same as the object representation that you would like to use in your application, as in the following XML example:

<AddressCard>

<FirstName>John</FirstName>

<LastName>Doe</LastName>

<Email>[email protected]</Email>

<StreetAddress>

<Address1>1666 Pudding Lane</Address1>

<City>London</City>

<Country>England</Country>

</StreetAddress>

</AddressCard>

Example 7-18. An XML-based example address card.

Creating an object class such as CAddressBookContact equipped with better methods that return items corresponding to those in the XML example above is quite simple: sub-class the CSenDomFragment as shown in the following (incomplete) code fragment:

AdressbookContact.h

// CONSTANTS

_LIT8(KElementNameN, ”N”);

_LIT8(KElementNameGiven, ”GIVEN”);

_LIT8(KElementNameFamily, ”FAMILY”);

_LIT8(KElementNameEmail, ”EMAIL”);

// CLASS DECLARATION

class CAdressbookContact: public CSenDomFragment {

TPtrC8 CAdressbookContact::FirstName() {

return ContentoOfChild(KElementNameN, KElementNameGiven);

}

TPtrC8 CAdressbookContact::ContentoOfChild(const TDesC8&

aParent,

Example 7-19. Subclassing CsenDomFragment to create a new object class.

In order to further parse the StreetAddress element in the example, the CAddressBookContact class could override the default StartElementL() and EndElementL() methods of the CSenDomFragment in order to create an object of a CStreetAddress class responsible for further parsing that structure (if necessary).

The class might be instantiated by the following piece of code illustrating the use of the CSenBaseFragment::StartElementL() method:

// If this is a contact card, parse it into CAddressBookContact // In most cases, namespace match is typically checked, too.

if (aLocalName == KCard) {

iErrorState = ENoError;

CAddressBookContact* delegate = CAddressBookContact::NewL();

CleanupStack::PushL(delegate);

Example 7-20. Using delegation to pick fragments of XML stream.

IMPORTANT NOTE

Care should be taken with this approach, as a CSenDomFragment constructs an in-memory XML node tree, which may exhaust all available memory. For Web service messages, which are typically quite small with little internal structure, this is usually not a major concern.

7.7.2.3

7.7.2.3 CSenBaseFragment CSenBaseFragment

The CSenBaseFragment class allows the developer to register an object that can be used to parse a particular XML entity present in an XML document. Using this class is like the traditional (and simpler) way to register a SAX event handler for the occurrence of the named XML entity. A developer may create sub-classes of this class to deal with the processing of specific XML entities (for example, the CSenSoapMessage utility class itself is descended ultimately from CSenBaseFragment). It is then possible to delegate parts of the XML parsing process to individual

classes responsible for different parts of the structure of the incoming message. The particular advantage of this approach is memory saved in only parsing the structures needed by the application (and avoiding the wrapping structures required but not necessarily important to the application developer).

This approach is analogous to the traditional SAX parsing method.

A sub-class of CSenBaseFragment may implement its own custom parsing routines (by overriding the StartElementL() and EndElementL() methods of the base class). The sub-class is then responsible for maintaining a state machine to track where the class is in the parsing process.

If an incoming message is known to contain several parts (or a particular class is only interested in a single part of the message) then the application developer can create a sub-class of CSenBaseFragment to parse the incoming message. Only the requested element (specified using local name, namespace, and ns prefix) is then saved by default (i.e., no child elements of the node). Implementing the StartElementL() and EndElementL() methods can then be used to create even more parsed nodes (possibly by creating other sub-classes of CSenBaseFragment).

As an example, let us consider the previous example where we used a CSenDomFragment to encapsulate a complete address card.

In that case, the whole XML representation of an AddressCard was converted into an in-memory XML nodeset. If, however, the application were only interested in the StreetAddress portion of the XML, a CSenBaseFragment could be constructed passing the local name StreetAddress. This object could then be used to analyze the whole message, parsing only the StreetAddress portion once its start tag was found.

As a final note, it is possible to easily produce a CSenBaseElement representation of a fragment by using the

CSenBaseFragment->AsElement() or CSenBaseFragment->ExtractElement() API methods to retrieve an element representation. The difference between these these two calls is that ExtractElement() actually transfers ownership of the memory resource associated with that element to the calling application – this can be important if the caller is, for example, expecting to control the garbage collection of that element.

Related documents