SDMParser

The parser (SDMParser class) is the core component of the package, it is responsible for processing XMLs. The actual parsing is done by the standard java SAX parser provided by the BlackBerry platform.

Parsing is generic in the sense that an arbitrary (well-formed) XML can be processed, and the information content is returned without any loss:
/**
 * Parses the stream source of an XML and converts it to a Java Object containing all
 * the information that were contained by the source XML.
 * 
 * @param xml
 *            A byte array that holds a syntactically valid XML.
 * @return ISDMParserDocument The Object representation of the parsed XML.
 * @throws SDMParserException
 *             If the XML source is invalid.
 * @throws IllegalArgumentException
 *             If the argument is null.
 */
public abstract ISDMParserDocument parseXML(byte[] xml) throws SDMParserException, IllegalArgumentException;

The ISDMParserDocument interface provides access to all the data stored in the XML. The API user constructs the path inside the XML to the given data (attribute or text value), then the following methods return their value:
/**
* Returns the string value of the sub-document contained by this object and accessible via the
* element names provided by the 'route' argument.
* 
* @param route
*            "/" separated route that leads to the object route must contain indexes as well.
*            Route must end with the index number which uniquely identifies an XML element.
*            route must start with "/"
* @return The string value of the XML element on the route. It returns null if route
*         does not identify a unique element.
*/
public abstract String getValue(String route);

/**
* Returns the string value of the XML attribute of the object accessible via the element names
* provided by the 'route' argument.
* 
* @param route
*            "/" separated route that leads to the object route must contain indexes as well.
*            Route must end with the index number which uniquely identifies an XML element and
*            after the element the attribute local name (field name) must be appended with
*            slash. Example route: "/element1/1/element2/5/element3/2/attributename"
* @param namespaceURI
*            The Namespace URI of the attribute, or the empty String if the attribute local
*            name has no Namespace URI.
* 
* @return The string value of the given attribute on the given route
*/
public abstract String getAttribute(String route, String namespaceURI) throws IllegalArgumentException;

However, for applications that communicate with the OData Protocol and that are working with OData objects, it is more suitable to use parser methods that provide OData objects (hierarchies).

There are specific parser methods for the document types that come in the OData Protocol responses. These are the service document, metadata document, open search description, error message, atom feed and entry:
/**
* Parses the SDMOData Service Document XML and converts it to an appropriate Java Object.
* 
* @param xml
*            The byte array that holds SDMOData Service Document XML
* @return ISDMODataServiceDocument The Object representation of SDMOData Service Document.
* @throws SDMParserException
*             If the XML source is invalid.
* @throws IllegalArgumentException
*             If the argument is null.
*/
public abstract ISDMODataServiceDocument parseSDMODataServiceDocumentXML(byte[] xml) throws SDMParserException, IllegalArgumentException;

/**
* Parses the SDMOData metadata XML and converts it to an appropriate Java Object.
* 
* @param xml
*            The byte array that holds SDMOData Schema XML
* @return ISDMODataSchema The Object representation of the SDMOData Schema.
* @throws SDMParserException
*             If the XML source is invalid.
* @throws IllegalArgumentException
*             If the argument is null.
*/
public abstract ISDMODataMetadata parseSDMODataMetadataXML(byte[] xml, ISDMODataServiceDocument svDoc) throws SDMParserException, IllegalArgumentException;
The service document XML has to be processed before the metadata, because metadata parsing needs the service document object.
/**
* Parses the SDMOData Open Search Description XML from stream and converts it to an *appropriate Java Object.
* 
* @param xml
*            The byte array that holds the SDMOData Open Search Description XML.
* @return ISDMODataOpenSearchDescription The Object representation of the SDMOData Open Search
*         Description.
* @throws SDMParserException
*             If the XML source is invalid.
* @throws IllegalArgumentException
*             If the argument is null.
*/
public abstract ISDMODataOpenSearchDescription parseSDMODataOpenSearchDescriptionXML(byte[] xml) throws SDMParserException, IllegalArgumentException;

/**
* Parses the SDMOData Error XML from stream and converts it to an appropriate Java Object.
* 
* @param xml
*            The byte array that holds the SDMOData Error XML.
* @return ISDMODataError The Object representation of the SDMOData Error.
* @throws SDMParserException
*             If the XML source is invalid.
* @throws IllegalArgumentException
*             If the argument is null.
*/
public abstract ISDMODataError parseSDMODataErrorXML(byte[] xml) throws SDMParserException;
There are also dedicated methods for feed and parsing, and the parser is also able to process entry XMLs. Both of them need the entity set object representing the collection container of the entry, so the parser has access to the metadata of the entry, which is needed for proper data parsing.
/**
* Parses OData XML structures from stream that represent either a single SDMOData Entry or a
* feed of several SDMOData entries.
* 
* @param xml
*            The byte array that holds the XML source of either a single SDMOData Entry or a
*            feed of several SDMOData entries.
* @return ISDMODataFeed The vector of the SDMOData Entries contained by the source XML.
* @throws SDMParserException
*             If the XML source is invalid.
* @throws IllegalArgumentException
*             If the argument is null.
*/
public abstract ISDMODataFeed parseSDMODataEntriesXML(byte[] xml, ISDMODataEntitySet eSet)
throws SDMParserException, IllegalArgumentException;

/**
* Parses an entry XML.
* 
* @param xml
*            byte array the data is read from
* @param eSet
*            the related entity type
* @return ISDMODataEntry
* @throws SDMParserException
* @throws IllegalArgumentException
*/
public ISDMODataEntry parseSDMODataEntryXML(byte[] xml, ISDMODataEntitySet eSet) throws SDMParserException, IllegalArgumentException;

All the OData related classes are descendants of the generic SDMParserDocument class, meaning that its low level data access methods can be applied for the OData classes as well. This feature is useful when some information from the XML files is not accessible through the high level interfaces.

The structure of the metadata classes is built according to the OData object hierarchy. The information is accessed from two XMLs, the service document and the metadata XML. The service document is parsed first, then the metadata. The ISDMODataMetada object, which is received from the parser after processing the metadata XML is the root of the hierarchy. From this starting point, you can browse the whole hierarchy. Furthermore, from each lower level object, you can access its parent using the public ISDMParserDocument getParent() method. The ISDMParserDocument is the parent of all OData classes, so the result can be type cast to the proper OData type.

Collections and entity sets are in one-to-one relationship, containing even partially overlapping meta information about the corresponding atom feeds. This relationship is implemented through their name attribute, their non-qualified name is the same. However, used as method parameters, collection name is always without namespace, while entity set name is prefixed with the corresponding schema namespace.

Parsing is done without any data loss, that is, all the information contained in the XML is preserved in the resulted data structures. In addition to these data structures, the complete XML is also preserved. This is useful when the objects are persisted, because it is more efficient to persist a simple string instead of a complex data structure. It is also an advantage when data is stored encrypted.

The only drawback of this solution is when data is restored from the persistent storage, the stored XMLs are parsed again. So this is an expensive operation and should be done as rarely as possible. To avoid degrading user experience, application developers should perform this operation (restore object structure from persistence) in a background thread.

Object Hierarchy

There are certain use cases, where OData entry objects and their XML representations have to be created on the client side. For this, the SDMDataEntry class provides the public constructor SDMDataEntry(ISDMODataEntitySet eSet), which creates an empty entry object so that its attributes have to be set one-by-one by calling the corresponding setter method. Finally, the public String toXMLString() method generates the XML representation of the entry object.