VisualAge Smalltalk XML mapping supports the following mapping types:
The attribute mapping maps an XML element that contains an attribute to an attribute of a Smalltalk class. A subelement that is a text element can also be mapped as an 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:"> <Attribute>Order.date</Attribute> </AttributeMapping> </ClassElementMapping>
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.
Subelement mapping maps an XML element to a class attribute. For example, if Customer is an attribute of the Order class, the Order element maps to the Order class and the Customer attribute of the Order class maps to the Customer element. The following example creates an instance of JrcOrder and the Customer attribute contains 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>
Subelement attribute mapping maps an XML element to the attribute of a subelement. 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>
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 attribute specification for lineItems returns true to the method representsCollection, the abtAddItem: method will be called to add the subelement instance of JrcLineItem to the collection.
If you have a class that behaves like a collection, implement the following methods:
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 >
The mapping function uses the attribute specifications contained in the class interface specification. 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 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.