Enhancing your applications : VA Smalltalk XML Support. : Using VA Smalltalk XML Support : Using the VA Smalltalk XML Mapping Parser

Using the VA Smalltalk XML Mapping Parser
Basic VA Smalltalk deserialization support allows the developer to map DOM objects into custom Smalltalk objects by applying mapping rules that are specified as instances of AbtXmlMappingSpec. These mapping rules can be read from an XML file. The VA Smalltalk interface specification of the Smalltalk object is used to determine the desired class for incoming XML elements and attributes. Incoming XML strings are converted to instances of an attribute class that is specified by the interface of the containing object.
Advances in XML make it possible to specify the data types of XML elements using an XML schema. DTD definitions are very useful and efficient when describing the structure of XML data. However, schemas provide an advantage over DTD definitions because schemas provide information about both the structure and type of XML data. Instead of relying on the DTD for the XML element structure and a VA Smalltalk interface specification for the data type, an XML schema can be used to provide both pieces of information. The VA Smalltalk mapping specification is required in either case to reconcile naming disparities between XML data and the class that represents the data.
The XML mapping parser (AbtXmlMappingParser) is used to parse XML data directly into Smalltalk domain objects by applying the rules specified in an XML schema definition and a VA Smalltalk XML mapping specification. Both the schema and the mapping specification are optional, and the parser results will vary depending upon which of these inputs are provided during parsing. The XML mapping parser should NOT be used to parse XML data that requires validation via an XML DTD.
By default, the result of parsing using the AbtXmlMappingParser is an instance of the class AbtXmlMappedRootElement. Subordinate XML elements are represented as instances of AbtXmlMappedElement. The API for these objects closely resembles the DOM API discussed previously. For XML elements that are mapped to domain objects, an AbtXmlMappedElement also holds a reference to the object that it maps into.
Input deserialization configuration
AbtXmlMappingParser is a customized SAX parser that can be configured to handle a variety of parsing scenarios. When instantiating an AbtXmlMappingParser, a deserialization configuration (AbtXmlDeserializationConfiguration) can be provided to customize the behavior of the parser. For example, it may be necessary to use a custom SAX handler to process the contents of a particular element. A custom deserialization configuration enables this and various other customizations. The example below builds a deserialization configuration for parsing WSDL (Web Service Definition Language) documents. The example code can be executed if the Web services features is loaded.
| rootConfig importConfig |
rootConfig := AbtXmlDeserializationConfiguration
newMappingParserConfiguration.
rootConfig
mappingSpecName: SstWebServicesParserApp abtXmlCacheKey.
 
"Set the SAX content handler to our special WSDL handler"
rootConfig handlerClass: SstWsdlSaxHandler.
 
" Create a new child that is used for handling WSDL 'import' elements "
" The handler class specifies importConfig as the configuration that
handles WSDL import elements"
importConfig := rootConfig newConfiguration.
importConfig
handlerClass: SstWsdlImportHandler.
rootConfig useConfiguration: importConfig
forElementNamed: 'import'
inNamespace: SstWebServicesParserApp abtXmlCacheKey.
 
" Add the configuration to the XML object cache so that it can be reused "
AbtXmlObjectCache current addDeserializationConfiguration: rootConfig
named: SstWebServicesParserApp abtXmlCacheKey
 
The AbtXmlMappingParser is most useful when both a mapping specification and schema can be resolved. However, the parser will function in the absence of these artifacts. Below is a summary of the parser behavior when the various XML parsing inputs are resolvable.
Schema and mapping specification are defined:
If parsing and mapping are successful, the resulting AbtXmlMappedElement will contain a domain object that was constructed from the contents of the XML.
Mapping is only necessary if you wish to construct custom objects from the parsed input. If a domain object was not constructed by the parser, the resulting AbtXmlMappedElement can act as a protocol object that understands the get and set selectors derived from the schema. The get and set selectors are derived from the mapping for the XML element.
Schema with no mapping specification
No schema with a mapping specification
Apply the rules of the mapping specification to determine the correct Smalltalk name for an element within the XML document. The XML name and the Smalltalk name are assumed to be the same if no mapping is specified.
For this case, the XML must be well-formed but does not need to conform to any specific shape. Data type conversion will occur only if an interface specification is specified for the target object; otherwise, all data is stored as Strings.
No schema and no mapping specification -
The resulting instance of AbtXmlMappedElement will contain the XML tree structure. All primitive data will be stored as strings. Get and set messages can be sent to the resulting instance for elements that have valid selector names. ie) 'abc:def' is a valid XML name but cannot be a selector.
Invocation
Application: AbtXmlMappingParserApp
" Create a new parser using the cached deserialization configuration"
| parser |
parser := AbtXmlMappingParser usingConfiguration:
( AbtXmlObjectCache current
deserializationConfigurationNamed: SstWebServicesParserApp abtXmlCacheKey )
parser parseURI: 'sstwsadm.wsdl'.
Using AbtXmlElement as Protocol Object
For objects that are used only for representing XML data, it may practical to utilize instances of AbtXmlMappedElement to represent the object structure and support object protocol. Instances of AbtXmlMappedElement can be treated like domain objects constructed dynamically from schema types. Get and set selectors for these protocol objects are derived from the element and attributes of an XML schema type. When an element mapping is present, the mapping can be used to specify the get and set selectors for the element.
Consider the following schema definition:
<xsd:complexType name="GetAuthToken">
<xsd:attribute name="generic" type="xsd:string" use="required" />
<xsd:attribute name="userID" type="xsd:string" use="required" />
<xsd:attribute name="cred" type="xsd:string" use="required" />
</xsd:complexType>
The example below assumes that the schema type GetAuthToken is defined in namespace urn:uddi-org:api. The code uses a protocol object to store and retrieve XML data.
| type protocolObject |
type := AbtXmlObjectCache current typeNamed: 'GetAuthToken' inNamespace: 'urn:uddi-org:api'.
protocolObject := type asMappedElement.
protocolObject generic: true ;
userID: 'VisualAge' ;
cred: 'password'.
Transcript cr;
show: 'generic -> ', protocolObject generic printString ;
cr;show: 'userID -> ', protocolObject userID printString ;
cr;show: 'cred -> ', protocolObject cred printString .
Resolving XML resources
The AbtXmlMappingParser must be able to resolve XML schemas and VA Smalltalk mapping specifications during parsing. These resources are retrieved from the XML object cache. By default, the global XML object cache is used to resolve required parsing resources; however, an alternative XML object cache can be specified in the deserialization configuration used by the parser.
Commonly used XML resources should be stored in the object cache before referencing their content. For some applications, it may be prudent to initialize XML resources using the #startup method of the application class. For example, the startup method shown below confirms the presence of certain entries in the global XML object cache. If a resource is not located in the cache, the resource is initialized. For the example below, the method #abtXmlCacheKey answers the XML namespace of the item being added.
startUp
" Add the required XML serialization and deserialization inputs to the AbtXmlObjectCache "
( AbtXmlObjectCache current schemaNamed: self abtXmlCacheKey) isNil
ifTrue: [ self abtXmlInitializeSchemas ].
( AbtXmlObjectCache current mappingSpecNamed: self abtXmlCacheKey ) isNil
ifTrue: [ self abtXmlInitializeMappingSpecs ].
( AbtXmlObjectCache current deserializationConfigurationNamed: self abtXmlCacheKey ) isNil
ifTrue: self abtXmlInitializeDeserializationConfigurations ].
Resources should typically be stored in the XML object cache keyed by XML namespace. During parsing, XML resources are looked up in the XML object cache as needed using the XML namespace of the element being parsed.
Handling errors
The XML mapping parser signals an SgmlException if errors are discovered while parsing an XML stream. Errors that occur while mapping XML elements into domain objects are reported using instances of the class AbtXmlMappingError. See link Handling Mapping Exceptions for additional information about mapping errors.
AbtXmlMappedContentHandler reports parse errors using the standard SAX interfaces 'parseError:' and 'warning:'. Applications can perform specific behavior by setting the #errorHandler: and #warningHandler: for the parser configuration. Mapping errors are reported by signalling an exception. Below are the current mapping errors: