Xmlite#
Xmlite reads and writes XML through plain Delphi objects: one class per element or type of the schema, one property per child element or attribute. It is namespace- and schema-aware, and parses in place on the text, without building a DOM.
Classes come from two places:
- generated by ProRocket Lite from an XSD or WSDL - exact to the schema, regenerated when it changes
- hand-written, for a small format without a schema or to start simple
Reading and writing#
uses System.SysUtils, System.IOUtils,
ProRock.Xmlite, // FromXml and ToXml (a class helper: visible only where ProRock.Xmlite is in uses)
ProRock.Xmlite.Schema.Order;
var order := TOrderE.Create; // the root element's class: <order> -> TOrderE
try
if order.FromXml(TFile.ReadAllText('order.xml', TEncoding.UTF8)) then // True when the text was parsed
Writeln(order.Customer.Name, ': ', order.Items.Item.Count, ' items');
order.Status := osShipped;
TFile.WriteAllText('order.xml', order.ToXml, TEncoding.UTF8);
finally
order.Free;
end;
- Nested objects and lists are created and freed with their parent (read-only properties); just use them.
- Enumerations are Delphi enums,
maxOccurs > 1gives a typed list (...List). TXmlite.ObjectToXml(obj)writes any object, also one that is not a root element.
What the generator makes of a schema#
From this part of an XSD:
<xs:simpleType name="OrderStatus">
<xs:restriction base="xs:string">
<xs:enumeration value="new"/>
<xs:enumeration value="shipped"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ItemsType">
<xs:sequence>
<xs:element name="item" type="ItemType" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
you get:
TOrderStatusST = (osNew, osShipped);
TItemsTypeCT = class(TXmliteComplexType)
private
fItem: TItemTypeCTList;
published
[TNaming(TNaming.nFlatCase)]
property Item: TItemTypeCTList read fItem;
end;
The suffix tells what a name was in the schema: E an element, CT a complex type, ST a simple type. One unit per XML
namespace (ProRock.Xmlite.Schema.<Name>), so the units of a schema set use each other as the schemas import each other.
| XSD | Delphi |
|---|---|
xs:string, xs:token, ... |
string types (TStringST = type string) |
xs:integer, xs:long, xs:int, xs:short, ... |
Int64 types |
xs:unsignedLong and its derived types |
UInt64 types |
xs:decimal, xs:float, xs:double |
Double types |
xs:boolean |
Boolean |
xs:date, xs:dateTime, xs:duration, ... |
string types: the text as in the document |
| enumeration | a Delphi enum |
| complex type, element | a class |
maxOccurs > 1 |
a typed list |
| simpleContent (text plus attributes) | the text in XmlValue, the attributes as properties |
Generated string types
TStringST is type string, so string-helper methods need a cast: string(response.Address).Replace(...).
Hand-written classes#
Without a schema, write the classes yourself. A published property is an attribute by default; [TXmliteElement] makes it a
child element:
uses ProRock.Basite, ProRock.Xmlite;
type
TItem = class(TBasite)
private
fSku: string;
fTitle: string;
fPrice: Double;
published
property sku: string read fSku write fSku; // <item sku="...">
[TXmliteElement]
property title: string read fTitle write fTitle; // <title>...</title>
[TXmliteElement]
property price: Double read fPrice write fPrice;
end;
TItems = class(TBasite)
private
fItem: TBasiteList<TItem>;
published
property item: TBasiteList<TItem> read fItem; // created and freed with TItems
end;
You can start with simple models like these and move to generated, namespace-aware classes when a schema arrives. The samples
ProRock.Xmlite.Basic (hand-written) and ProRock.Xmlite.Namespaces (generated) show both.
Derived types: xsi:type#
When the schema allows a derived type in place of the declared one, the document says so with xsi:type. To write it, tell the
element which class it holds:
props.Modified.SetXsiType(TW3cdtfCT); // <dcterms:modified xsi:type="dcterms:W3CDTF">
SetXsiType refuses a class that is not a complex type derived from the element's type. See
XML concepts for what this means.
Under the hood#
TXmlite and TXmliteList inherit from TBasite and TBasiteList. Three parts fit together:
- Basite defines how objects live: plain Delphi objects (PODOs), nested objects created and freed with their parent, independent of any data format
- MetaBank defines how they are described: the metadata of every registered class and property, built once and reused for every parse and write, so there is no repeated RTTI scanning. The classes of one schema registered there form its metamodel
- Xmlite defines how XML maps to them: elements, attributes and namespaces as metadata on top. Soapite builds on Xmlite
The metamodels that ship with ProRock - XML Schema itself, WSDL, the SOAP envelopes and bindings
(ProRock.Xmlite.Schema.*) - are ordinary generated units: ProRocket Lite generates them from their official schemas with
every release, and reads every XSD and WSDL through them.
Limits of the current version: Limitations and notes.