XML Mapping Components
VA Smalltalk XML mapping supports the following mapping types:
Mapping elements to class instances
The mapping DTD specifies an element named 'ClassElementMapping' which contains the rules for mapping an XML element into a Smalltalk class. The ClassElementMapping specifies which Smalltalk class is used to represent a particular XML element. Additionally, the ClassElementMapping contains a collection of AttributeMapping elements that describe how child XML elements and attributes are represented in the mapped class.
Mapping schema types to class instances
For optimal reuse when using schemas, Smalltalk class names can be mapped to XML schema 'types' rather than the actual 'elements'. Conceptually, a schema 'type' is like a Smalltalk class definition, and a schema 'element' is like an instance of a class. One schema type can be used to describe multiple schema elements. The same type mapping can be shared by multiple XML elements that have different names but share the same schema type. The mapping specification DTD defines a tag named 'ClassTypeMapping' to allow this type of definition. Semantics and behavior of the ClassTypeMapping are the same as for the ClassElementMapping.
<ClassTypeMapping TypeName="SstWSPerson" ClassName="SstWSPerson" />
The ClassTypeMapping is only relevant when used in conjunction with XML schemas. To determine the mapping for an XML element, the following logic is applied:
•Search for a ClassElementMapping with ElementTagName that matches the target element
•If no mapping found, search for a ClassTypeMapping with TypeName that matches the schema type of the target element
Using the above logic allows a generic mapping to be created for a schema type, but the generic mapping can be overridden by supplying mappings for individual elements when necessary.
Mapping Attributes
The attribute mapping contains rules for mapping from an XML attribute or simple element to a Smalltalk attribute. For example, if number is an attribute of Order and Order.date is a subelement that is a text element, both number and Order.date can be mapped using an attribute mapping.
<?xml version="1.0"?>
<!DOCTYPE Order SYSTEM "OrderEnt.dtd">
<Order number="O00001" status="Open">
<Order.date>2000-02-14</Order.date>
</Order>
The class element mapping follows:
<ClassElementMapping ElementTagName="Order" ClassName="JrcOrder">
<AttributeMapping ClassAttribute="orderNumber">
<Attribute>number</Attribute>
</AttributeMapping>
<AttributeMapping
ClassAttribute="datePlaced"
AttributeClassCreationMethod="dateFromIbmIsoString:"
ObjectToStringConversionMethod="printAsIbmIsoString">
<Attribute>Order.date</Attribute>
</AttributeMapping>
</ClassElementMapping>
The previous example demonstrates several of the useful capabilities of an AttributeMapping. The mechanism for storing and retrieving data from the Smalltalk object is defined using the 'ClassAttribute' setting or by specifying the 'GetSelector' and 'SetSelector' settings.
Use the 'ClassAttribute' setting if your class defines a VA Smalltalk interface specification that describes its public protocol.
Use the 'GetSelector' and 'SetSelector' settings if no VA Smalltalk interface is defined for the target class. Conveniently, the 'SetSelector' can also be used to specify a one-argument collection manipulation method as in the example below.
<ClassTypeMapping TypeName="MyObjectCollection" ClassName="OrderedCollection">
<AttributeMapping SetSelector="addAll:">
<SubElement>MyElement==/SubElement>
</AttributeMapping>
</ClassTypeMapping>
The 'AttributeClassCreationMethod' specifies a one-argument class method that is used to instantiate the target class. The expected argument is the XML string value for the element or attribute. Since the target class is determined from the VA Smalltalk interface specification, the 'AttributeClassCreationMethod' is only used when an interface specification is defined for the parent object.
The 'ObjectToStringConversionMethod' specifies the name of a method to perform against the target object which returns an XML string representing the target object. The setting is used during serialization but is ignored when serialization uses an XML schema.
The attribute specification for the datePlaced feature of the JrcOrder class has the attribute class Date. The attribute class creation method AttributeClassCreationMethod, specifies the name of a Date class method. The method is set to dateFromIbmIsoString: in order to create the string as a date. The string in the XML will be passed as the argument.
Note:
The #abtXmlFromString: class method provides an alternative way to map strings. The #abtXmlFromString: method can be overridden to create an instance of a simple object from a passed string. The #abtXmlFromString: method is invoked by default when no AttributeClassCreationMethod or StringConversionMethod method is specified in an attribute mapping.
Mapping Subelements
Subelement mapping maps a complex XML element to a class attribute. For example, if customer is an attribute of the Order class, the JrcOrder element maps to the JrcOrder class and the customer attribute of the Order class maps to the Customer element. The following mapping example can be used to map corresponding XML into an instance of JrcOrder with customer attribute set to an instance of JrcCustomer:
<ClassElementMapping ElementTagName="Order" ClassName="JrcOrder">
<AttributeMapping ClassAttribute="orderNumber">
<Attribute>number</Attribute>
<AttributeMapping ClassAttribute="customer">
<SubElement>Customer</SubElement>
</AttributeMapping>
</ClassElementMapping>
<ClassElementMapping ElementTagName="Customer" ClassName="JrcCustomer">
<AttributeMapping ClassAttribute="number">
<Attribute>number</Attribute>
<AttributeMapping>
<AttributeMapping ClassAttribute="name">
<Attribute>name</Attribute>
</AttributeMapping>
</ClassElementMapping>
Mapping Subelement Attributes
Subelement attribute mappings are used to map attributes or subelements of a child XML element into attributes of a base Smalltalk object instance. For example, the CustomerId subelement contains the name and number of the customer. There is no corresponding CustomerId class in Smalltalk. The name and number of the customer are part of the Customer class. Subelement attribute mapping maps the name and number attributes to the Customer class.
<?xml version="1.0"?>
<!DOCTYPE Order SYSTEM "OrderEnt.dtd">
<Order number="O00001" status="Open">
<Customer>
<CustomerID name="Joe Smith" number="C135">
<! other customer stuff deleted >
</Customer>
<Order.date>2000-02-14</Order.date>
</Order>
The class element mapping follows:
<ClassElementMapping
ElementTagName="Customer"
ClassName="JrcCustomer">
<AttributeMapping ClassAttribute="number">
<SubElement>CustomerID</SubElement>
<Attribute>number</Attribute>
</AttributeMapping>
<AttributeMapping ClassAttribute="name">
<SubElement>CustomerID</SubElement>
<Attribute>name</Attribute>
</AttributeMapping>
</ClassElementMapping>
Mapping Collections
Subelement mappings can map an XML element to a class attribute that is a collection, or a class that demonstrates collection behavior.
For example, an Order contains a collection of line items.
<ClassElementMapping
ElementTagName="Order"
ClassName="JrcOrder">
<AttributeMapping ClassAttribute="lineItems">
<SubElement>LineItem</SubElement>
</AttributeMapping>
</ClassElementMapping>
<ClassElementMapping ElementTagName="LineItem" ClassName="JrcLineItem">
<AttributeMapping ClassAttribute="quantity" StringConversionMethod="asNumber">
<Attribute>Quantity</Attribute>
</AttributeMapping>
<AttributeMapping ClassAttribute="item">
<SubElement>Item</SubElement>
</AttributeMapping>
</ClassElementMapping>
The LineItem element maps to the JrcLineItem class. The lineItems class attribute should contain a collection of JrcLineItem. If the lineItems attribute of JrcCustomer maps to a class that answers true to the abtImplementsCollectionMapping, then the method abtAddItem: is invoked during serialization to add a newly serialized instance of JrcLineItem to the lineItems collection.
If you have a class that behaves like a collection, implement the following methods:
abtAddItem:anArray
The anArray class contains index, key and subelement instances.
abtImplementsCollectionMapping
This method answers a boolean indicating whether the receiver supports collection mapping protocol.
Note:
These methods are provided in the Smalltalk Collection hierarchy.
Mapping collections that contain wrapper element tags
The previous section discussed how to map XML element collections into Smalltalk attributes. It is very common for XML authors to delineate collections using an XML wrapper element.
For example, the LineItems wrapper element below contains a collection of LineItem elements.
<LineItems>
<LineItem number="1">
<Quantity>12==/Quantity>
<Item number= "I12345" >
<Description>Roses==/Description>
<UnitPrice>$3.50==/UnitPrice>
</Item>
</LineItem>
<LineItem number="2">
<Quantity>1==/Quantity>
<Item number="I67890">
<Description>Box of Candy==/Description>
<UnitPrice>$5.50==/UnitPrice>
</Item>
</LineItem>
<LineItems>
Applications typically won't have a use for wrapper elements like 'LineItems' since it is merely used as a container for the collection of 'LineItem'. Subelement attribute mappings provides a nice mechanism to process the collection of 'LineItem' elements without the need to directly map the 'LineItems' wrapper element. The attribute mapping for this scenario is as follows:
<AttributeMapping ClassAttribute="lineItems">
<SubElement>LineItems</SubElement>
<Attribute>LineItem</Attribute>
</AttributeMapping>
Mapping to a Dictionary
Mapping subelements to a dictionary is similar to mapping collections except that you must specify a key attribute. The mapping specification indicates which attribute of the subelement must be used as the key in the dictionary.
The following example uses a dictionary to store transaction information. The dictionary uses a key datetime, which is the date and time, to store each record. A record is an instance of CompanyInfo, which contains the user name and the amount of the transaction. The example XML source follows:
<?XML VERSION="1.0" ?>
<!DOCTYPE bips SYSTEM "d:\bips.dtd">
<bips version="1.0">
<payment-response>
<payment-response-id>1</payment-response-id>
<statement datetime = "6/6/2000-11:38:37 AM">
<description>Checking</description>
<contextual-info>113</contextual-info> "amount in checking"
</statement>
<statement datetime = "6/6/2000-11:38:37 AM">
<description>Cable Company</description>
<contextual-info>-14182</contextual-info> "balance with cable company"
</statement>
</payment-response>
</bips>
The example mapping specification follows:
<?xml version="1.0"?>
<!DOCTYPE XmlMappingSpec SYSTEM "map.dtd" >
<XmlMappingSpec Name="CustomerMappings">
<ClassElementMapping ElementTagName="bips" ClassName="ResponseObject" >
<AttributeMapping ClassAttribute="transDictionary">
<SubElement Key="datetime">statement</SubElement>
</AttributeMapping>
</ClassElementMapping>
<ClassElementMapping ElementTagName="statement" ClassName="CompanyInfo" >
<AttributeMapping ClassAttribute="name">
<Attribute>description</Attribute>
</AttributeMapping>
<AttributeMapping ClassAttribute="amount">
<Attribute>contextual-info</Attribute>
</AttributeMapping>
</ClassElementMapping>
</XmlMappingSpec >
Specifying Type Information
In order to properly convert simple XML strings into instances of the desired Smalltalk object, type information must be supplied to the VA Smalltalk XML support. In VA Smalltalk, there are currently two ways to associate a Smalltalk type with an XML element or attribute.
•Provide a VA Smalltalk interface specification for the Smalltalk object being mapped.
•Provide an XML schema for the XML being mapped.
Specifying Interface Specifications
The application controls the data types by supplying attribute specifications that describe the content of a class. However, not all classes have an interface specification. You can use XML to specify interface specifications for classes that do not contain them. The following DTD, abtcldef.dtd, is provided:
<!ELEMENT Model (ClassDefinition)*>
<!ENTITY % FeatureSpec
'(AttributeSpec | ActionSpec | EventSpec)'>
<!ELEMENT ClassDefinition InterfaceSpec>
<!ATTLIST ClassDefinition
Name NMTOKEN #REQUIRED>
<!ELEMENT InterfaceSpec %FeatureSpec;*>
<!ELEMENT AttributeSpec EMPTY>
<!ATTLIST AttributeSpec
Name NMTOKEN #REQUIRED
Class NMTOKEN #REQUIRED
GetSelector NMTOKEN #IMPLIED
SetSelector NMTOKEN #IMPLIED>
<!ELEMENT ActionSpec EMPTY>
<!ELEMENT EventSpec EMPTY>
In the above example, the ActionSpec and EventSpec elements are not currently used.
For the AttributeSpec element, get and set selectors are optional. If get and set selectors are not provided, the Name attribute is used to create default selectors. For example, if the AttributeSpec element has a Name of number, the GetSelector defaults to number and the SetSelector defaults to number:
The instance method privateInterfaceSpec can be overridden in order to answer the parsed interface specification.
The following Smalltalk code reads interface specifications into the cache:
| domSource |
domSource :=
(AbtXmlDOMParser newValidatingParser)
parseURI: 'E:\xmlJim\ClassModel.xml'.
AbtXmlObjectCache current addInterfaceSpecsFromModelDOM: domSource.
Specifying XML Schemas
Another way to supply type information to the VA Smalltalk XML support is by constructing an XML schema that describes the interface for the XML being consumed or serialized. XML schemas provide a standard way of externally describing the structure and types for the objects in your system. The data provider controls the data types for the XML data and the VA Smalltalk XML support uses the supplied schema data types to create instances of corresponding Smalltalk objects. To gain a basic understanding of XML Schemas, go to the link
http://www.w3.org/TR/xmlschema-0/. Support for XML schemas is discussed in more detail in the section
Using the VA Smalltalk XML Mapping parser.