Liquid XML Data Binder (C++, Java, VB6) / Help With Your Evaluation / What is XML Data Binding / XML Data Binding - In More Detail
In This Topic
    XML Data Binding - In More Detail
    In This Topic
     


    XML Data Binding - In Detail

    This section provides a more detailed look at the nuts and bolts of going from an XSD to code.

    Part 1 - What is XML Data Binding? - an introduction to the technology.
    Part 2 - The Product - a summary of the product.
    Part 3 - In Detail - a more in depth look at what is produced when code is generated from a schema.

    XML Data Binding - In More Detail

    As you have already seen using the code generated from a data binding tool can greatly reduce the amount & complexity of the code you have to write when dealing with XML. If you have a complex schema, and are not XSD experts the benefits are clear.
    In this next section we will look at the code produced for a number of XSD constructs, we have chosen C# as the language as it is simpler to read, however the output for Java, C++ & VB6 all take the same form, and the code required to use the generated code is almost identical (once the syntax of the languages is taken into account).

    Elements Examined


    Sequence

    A sequence describes an element, and defines that all child elements must appear (if mandatory) and they must appear in the correct order.

    Sample XSD

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
        <xs:element name="ParentSeq">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="FirstChild" type="xs:string"/>
                    <xs:element name="SecondChild" type="xs:string"/>
                    <xs:element name="ThirdChild" type="xs:string"/>
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    </xs:schema>

    Generated Code

    This is a UML representation of the generated code, note that there are 3 string properties (FirstChild, SecondChild & ThirdChild) corresponding with the child elements within ParentSeq.

    Sample Code - Creating an XML document using the generated classes

    // create an instance of the class to< load the XML file into
    SequenceLib.ParentSeq elm = new SequenceLib.ParentSeq();
    // Set data into element
    elm.ThirdChild = "Some Data 3";
    elm.FirstChild = "Some Data 1";
    elm.SecondChild = "Some Data 2";
    // Lets see what we've got
    Trace.WriteLine(elm.ToXml());

    Xml Created

    <?xml version="1.0"?>
    <!--Created by Liquid XML Data Binding Libraries (www.liquid-technologies.com) for Liquid Technologies Ltd-->
    <ParentSeq xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
        <FirstChild>Some Data 1</FirstChild>
        <SecondChild>Some Data 2</SecondChild>
        <ThirdChild>Some Data 3</ThirdChild>
    </ParentSeq>

    Notes

    It does not matter the order in which the child elements are set they will appear in the output XML correctly.
    If the child elements are not in the correct order when an XML file is read in, then an exception is raised.
    The element all works in the same way as sequence, the elements are written out in the order they where defined, but when they are read in they can be in any order.


    Choice

    A choice describes an element, and defines that only one of the child elements can appear.

    Sample XSD

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
        <xs:element name="ParentSeq">
            <xs:complexType>
                <xs:choice>
                    <xs:element name="FirstChild" type="xs:string"/>
                    <xs:element name="SecondChild" type="xs:string"/>
                    <xs:element name="ThirdChild" type="xs:string"/>
                </xs:choice>
            </xs:complexType>
        </xs:element>
    </xs:schema>

    Generated Code

    This is a UML representation of the generated code, note that there are 3 string properties (FirstChild, SecondChild & ThirdChild) corresponding with the child elements within ParentSeq. There are also 3 other properties IsValidFirstChild, IsValidSecondChild &IsValid ThirdChild, these indicate whether the corresponding property contains a valid value. The property ChoiceSelectedElement, indicates which of the 3 elements is selected at any given time. If one element is selected (i.e. FirstChild contains a value), and then another is given a value (say SecondChild is set to "Some Text"), then FirstChild will become invalid (IsValidFirstChild will return false, and reading from FirstChild with raise an exception), and ChoiceSelectedElement will reference SecondChild.

    Sample Code - reading an XML Document from a file


    // create an instance of the class to load the XML file into
    choiceLib.ParentChoice elm = new choiceLib.ParentChoice();
    elm.FromXmlFile("c:\\Choice.xml");
    // we can find out child child element is selected using ChoiceSelectedElement
    if (elm.ChoiceSelectedElement == "SecondChild")
    {
        Trace.Write("The second child element was present and has the value " + elm.SecondChild);
    }
    // or by looking at the IsValid flags
    Debug.Assert(elm.IsValidFirstChild == false);
    Debug.Assert(elm.IsValidSecondChild == true);
    Debug.Assert(elm.IsValidThirdChild == false);

    Notes

    If more than one child element is selected in the XML then the FromXmlFile will raise an exception.


    Primitive and Complex Types

    We.ve now covered how the basic constructs (all/sequence/choice) are represented. However all the child items used have all been of type string.
    This section will explore other types, and show how other more complex child elements can be manipulated.
    Download Sample file & generated output (Source ~0.5 MB)

    Sample XSD


    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
        <xs:element name="RootElm">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="StringType" type="xs:string"/>
                    <xs:element name="intType" type="xs:int"/>
                    <xs:element name="ComplexType">
                        <xs:complexType>
                            <xs:sequence>
                                <xs:element name="DateType" type="xs:dateTime"/>
                                <xs:element name="Base64Type" type="xs:base64Binary"/>
                            </xs:sequence>
                        </xs:complexType>
                    </xs:element>
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    </xs:schema>

    Classes Created


    public class RootElm : LiquidTechnologies.LtXmlLib20.XmlObjectBase
    {
        public String StringType { get... set...}
        public Int32 IntType { get... set...}
        public TypesLib.ComplexType ComplexType { get... set...}
    }
    public class ComplexType : LiquidTechnologies.LtXmlLib20.XmlObjectBase
    {
        public LiquidTechnologies.LtXmlLib20.XmlDateTime DateType { get... set...}
        public LiquidTechnologies.LtXmlLib20.BinaryData Base64Type { get... set...}
    }

    Here 2 classes have been created RootElm & ComplexType.

    Sample Code - creating an XML document


    // create an instance of the class to load the XML file into
    TypesLib.RootElm elm = new TypesLib.RootElm();
    // set data into the element
    elm.StringType = "Test String value";
    elm.IntType = 5;
    // and the child element
    elm.ComplexType.DateType.SetDateTime(2004, 4, 26, 10, 41, 35);
    elm.ComplexType.Base64Type.SetData("075BCD15", BinaryData.Encoding.Hex);
    // Lets look at the XML we produced.
    Trace.WriteLine(elm.ToXml());

    XML Produced


    <?xml version="1.0"?>
    <!--Created by Liquid XML Data Binding Libraries (www.liquid-technologies.com) for Liquid Technologies Ltd-->
    <RootElm xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
        <StringType>Test String value</StringType>
        <intType>5</intType>
        <ComplexType>
            <DateType>2004-04-26T10:41:35</DateType>
            <Base64Type>cLXcUQ==</Base64Type>
        </ComplexType>
    </RootElm>

    Notes

    If the ComplexType held within the RootElm was optional, then you would have to create and assign an object to elm.ComplexType before using it (see next item).


    Cardinality

    In this sample, the sequence contains a number of child elements. The child elements all have different cardinality (changed by setting the minOccurs and maxOccurs attributes, the default for both is 1). The generator deals with these flags in 3 different ways.
    Download Sample file & generated output (Source ~0.5 MB)

    Mandatory - minOccurs=1 and maxOccurs=1
    If the child element is a primitive (string, short time etc) then a get & set accessor is provided. The value held must always contain a valid (non null) value.
    If the child element is another complex element (i.e. represented as new class in the generated code), then a get accessor is provided. This will always return a valid object.

    Optional - minOccurs=0 and maxOccurs=1
    If the child element is a primitive (string, short time etc) then a get & set and IsValid accessor is provided. While IsValid is true the value held must always contain a valid (non null) value. If IsValid is set to false then the get accessor will fail if called, and no child element is created in the XML.
    If the child element is another complex element (i.e. represented as new class in the generated code), then a get & set accessor is provided. This will initially be null. If the child element is required in the XML, then a new child object must be created (new XXX()), and assigned to the property, it can be removed by setting the value to null.

    Collection - minOccurs=n and maxOccurs= >1
    If the child element is a primitive (string, short time etc) then a get accessor is provided. The object returned from the get operator is a collection of primitive types, if the collection is empty then no items appear in the XML.

    If the child element is another complex element (i.e. represented as new class in the generated code), then a get accessor is provided. This returns an object that represents a collection of the complex elements.

    Sample XSD


    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
        <xs:element name="Cardinality">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="MandatoryChild" type="xs:string"/>
                    <xs:element name="OptionalChild" type="xs:string" minOccurs="0"/>
                    <xs:element name="CollectionChild" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    </xs:schema>

    Generated Code

    As you can see there are 3 main properties within the class (MandatoryChild, OptionalChild & CollectionChild).
    The OptionalChild can be present in the XML or missing, because the item is a primitive, and can not be set to null (at least not in all languages) there is an additional property IsValidOptionalChild. This is a boolean field indicating if the value of OptionalChild is valid. If IsValidOptionalChild is false, and an attempt is made to read OptionalChild, then an exception is raised. IsValidOptionalChild can also be set to false in order to remove the element from the XML output.
    The MandatoryChild has to be present, and so there is no need for an IsValidMandatoryChild property. Attempts to set a null value into MandatoryChild will cause an exception to be raised.
    The CollectionChild references a collection (in this case a collection of strings), the property will never be null, however the collection may be empty.


    Sample Code - Creating an XML Document


    // create an instance of the class to load the XML file into
    CardinalityLib.Cardinality elm = new CardinalityLib.Cardinality();
    // Write data into the element
    elm.MandatoryChild = "Some value";
    // set some data into the optional element
    elm.OptionalChild = "Some other data";
    // if we change our mind we can remove this element from the output
    elm.IsValidOptionalChild = false;
    // The collection element contains a child collection (the
    // collection is always populated)
    elm.CollectionChild.Add("First item in collection");
    elm.CollectionChild.Add("Second item in collection");
    // Lets look at the XML we've just created
    Trace.WriteLine(elm.ToXml());
    // Reading from the element
    Trace.Write("Mandatory element contains value - " + elm.MandatoryChild);
    if (elm.IsValidOptionalChild == true)
        Trace.Write("Optional element present. Value - " + elm.OptionalChild);
    else
        Trace.Write("The optional element is not present");
    Trace.Write("Child elements in the Collection element");
    foreach(string val in elm.CollectionChild)
        Trace.Write(" value - " + val);

    Xml Created


    <?xml version="1.0"?>
    <!--Created by Liquid XML Data Binding Libraries (www.liquid-technologies.com) for Liquid Technologies Ltd-->
    <Cardinality xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
        <MandatoryChild>Some value</MandatoryChild>
        <CollectionChild>First item in collection</CollectionChild>
        <CollectionChild>Second item in collection</CollectionChild>
    </Cardinality>

    Notes

    We use the IsValidOptionalChild property to determine if the OptionalChild element was present in the XML.


    Extension

    A base complex type can be extended, the concept is similar to that of inheritance in C# C++ Java etc. In this sample we define a base complex type 'BaseComplexType', and derive from it 2 other complex types 'DerivedComplexType1' & 'DerivedComplexType2'.
    Finally we define an element 'UsingElement' that contains an element of type 'BaseComplexType'. In this element wherever we see 'BaseComplexType', we can use either 'DerivedComplexType1' or 'DerivedComplexType2'.

    Sample XSD


    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
        <xs:complexType name="BaseComplexType">
            <xs:sequence>
                <xs:element name="ChildOfBaseType" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
        <xs:complexType name="DerivedComplexType1">
            <xs:complexContent>
                <xs:extension base="BaseComplexType">
                    <xs:sequence>
                        <xs:element name="ChildOfDerivedType1" type="xs:string"/>
                    </xs:sequence>
                </xs:extension>
            </xs:complexContent>
        </xs:complexType>
        <xs:complexType name="DerivedComplexType2">
            <xs:complexContent>
                <xs:extension base="BaseComplexType">
                    <xs:sequence>
                        <xs:element name="ChildOfDerivedType2" type="xs:string"/>
                    </xs:sequence>
                </xs:extension>
            </xs:complexContent>
        </xs:complexType>
        <xs:element name="UsingElement">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="BaseType" type="BaseComplexType"/>
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    </xs:schema>



    Generated Code

    As you can see a class is created for every element defined within the schema. In order to allow any element that extends BaseComplexType, to be used wherever BaseComplexType is referenced (i.e. in UsingElement) an interface has been created (IBaseComplexType). This is implemented by all classes capable of being used where BaseComplexType appears.



    Sample Code - Creating an XML Document

    // create an instance of the class to load the XML file into
    ExtensionLib.UsingElement elm = new ExtensionLib.UsingElement();

    //////////////////////////////////////
    // Write Data into the new element
    // Use the element DerivedComplexType2 in the base element
    // UsingElement.BaseType where a BaseComplexType is exected
    ExtensionLib.DerivedComplexType2 elmDerv2 = new ExtensionLib.DerivedComplexType2();
    elmDerv2.ChildOfBaseType = "Data field From Base";
    elmDerv2.ChildOfDerivedType2 = "Data field From Derived Class";
    elm.BaseType = elmDerv2;

    // Look at the XMl we just created
    Trace.WriteLine(elm.ToXml());

    //////////////////////////////////////
    // Read data from the element
    // The object we get from elm.BaseType is exposed via
    // an interface common to all the objects that can
    // be used in its place (IBaseComplexType).
    Trace.WriteLine("Data from the Base class - " + elm.BaseType.ChildOfBaseType);

    // The actual object held in elm.BaseType can be either
    // DerivedComplexType1, DerivedComplexType2 or BaseComplexType
    // we need to use runtime type info to find out.
    if (elm.BaseType.GetType().Name == "DerivedComplexType2")
    {
        // now we know the type, we can cast it up accordingly
        ExtensionLib.DerivedComplexType2 elmDerv =(ExtensionLib.DerivedComplexType2)elm.BaseType;
        // and then make use of the properties defined in the derived class
        Trace.WriteLine("Data in DerivedComplexType2.ChildOfDerivedType2 class - " + elmDerv.ChildOfDerivedType2);
    }

    Xml Created


    <?xml version="1.0"?>
    <!--Created by Liquid XML Data Binding Libraries (www.liquid-technologies.com) for Liquid Technologies Ltd-->
    <UsingElement xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
        <BaseType xs:type="DerivedComplexType2">
            <ChildOfBaseType>Data field From Base</ChildOfBaseType>
            <ChildOfDerivedType2>Data field From Derived Class</ChildOfDerivedType2>
        </BaseType>
    </UsingElement>

    Notes