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:
• Apply the rules specified by the schema to make sure the structure of the data is correct. Schema information is retrieved from the XML object cache.
• Use the schema to determine the types for the incoming data and convert simple XML types to the corresponding Smalltalk objects.
• Apply the rules of the mapping specification to determine the correct Smalltalk name for an element or type within the XML document. The XML name and the Smalltalk name are assumed to be the same if no mapping is specified.
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
• Apply the rules specified by the schema to make sure the structure of the data is correct.
• Use the schema to determine the types for the incoming data and convert simple XML types to the corresponding Smalltalk objects.
• Complex types can be converted to Smalltalk domain objects only if there is a corresponding class in the Smalltalk image. For example, if the XML has a complex type called 'Employee', then a Smalltalk class called 'Employee' must exist. Set selectors with derived Smalltalk names must exist in the target instance. For example, an XML element named 'listprice' requires a set selector named #listprice:. A mapping specification should be provided if the mapped Smalltalk class does not directly correlate to the XML.
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:
• ##TypeSequenceMismatch - the elements in the XML did not conform to its schema
• ##ClassNotDefined - could not find a class to map this XML
• ##AttributeNotDefined - occurs when an XML element does not have a corresponding attribute in the target class instance
• ##SelectorNotFound - occurs when an attempt is made to set an attribute value, but the set selector is not found
• ##MissingAttribute - A required XML attribute was not found in the active element.
• ##MissingRequiredElement - An required element was not found in the XML stream
• ##CollectionNotExpected - The XML stream contains a collection of elements, but the XML schema element is not defined as a collection.