Customizing the XML format for your object
[XML]

This documentation module explains how to customize the XML format that corresponds to your object. More...

Classes

class  CLAM::XmlStorage
 Provides XML format storage for CLAM Component's. More...

Modules

 Using XML Adapters to implement StoreOn and LoadFrom
 

How to use XmlAdapters to implement Load and Store methods for a CLAM::Component.



Detailed Description

This documentation module explains how to customize the XML format that corresponds to your object.

The defaults for dynamic types

CLAM::DynamicTypes have automatic support for XML without any extra effort than defining the DynamicType itself. So, before attempting to generate your own format it is important to know whether you need to. Any DynamicType has a default XML implementation. So any CLAM::ProcessingData and any CLAM::ProcessingConfig will have such a default implementation.

By default, every dynamic attribute that is XML aware is dumped in sequence order, within an XML element that takes the attribute name as tag name. Because dynamic attributes may be instantiated or not, removed attributes are ignored on storing and, on loading, not present attributes are 'Removed' from the dinamic type.

So, which attributes are XML aware?

Basic objects are C primitive types and some others (std::string, CLAM::Complex<TData>, CLAM::Polar<TData>, CLAM::Point<TData>...) they use their extraction (>>) and insertion (<<) operator to generate plain content. You can define your own basic types.

Some words about strings attributes

Never use char* as attributes if you want to load it. Use some other alternative:

All of those alternatives can be used as 'basic types'.

STL like Containers

STL compliant containers have XML support if they are declared as DYN_CONTAINER_ATTRIBUTE.

When the contained class is a component, then each of the contained objects are stored as elements inside the container element. So:

 DYN_CONTAINER_ATTRIBUTE(1, public, std::list<MyComponent>, ComponentList, AComponent);

will look like

 <ComponentList size='4'>
    <AComponent> ... </AComponent>
    <AComponent> ... </AComponent>
    <AComponent> ... </AComponent>
    <AComponent> ... </AComponent>
 </ComponentList>

When the contained class is a basic type, all the container items will be stored in a single XML element separated by spaces.

 DYN_CONTAINER_ATTRIBUTE(1, public, std::vector<double>, LeafList, Ignored);

will look like

 <LeafList size='256'>342.243 2342.252 .... 0.234 0 0</LeafList>

Note that in this case the last macro parameter is ignored.

This is what you can get by default from a DynamicType. If you don't like it keep reading this documentation module.

Defining your own basic types

You can define any class, for example MyBasicType, to be used in XML as a basic type doing the following:

Customization Basics

Let see a sample Dynamic Type class:

 class ConcreteDT : public CLAM::DynamicType
 {
 public:
        DYNAMIC_TYPE(ConcreteDT, 5);
        DYN_ATTRIBUTE      (0, public, DummyComponent, MyComponent);
        DYN_ATTRIBUTE      (1, public, CLAM::Array<Complex>, MyArray);
        DYN_ATTRIBUTE      (2, public, FooDTClass, MyDynType);
        DYN_CONTAINER_ATTRIBUTE(3, public, std::list<int>, MyList);
        DYN_ATTRIBUTE      (4, public, int, MyInt);
 public:
        virtual ~ConcreteDT() {}
 protected:
        void DefaultInit()
        {
                AddAll();
                UpdateData();
        }
 // Some non dynamic attributes
 private:
        FooComponent mExtraNonDynamicAttribute;
 };

This Dynamic Type, as is, will generate default XML. In order to customize it we have to redefine two storage related methods:

 void MyDyn::StoreOn(Storage & s);
 void MyDyn::LoadFrom(Storage & s);

When a MyDyn is stored/loaded on/from a Storage, and the Storage detects that it is a component, it calls those functions in order to store/load all meaningful MyDyn subparts if it has any. So by redefining those functions we will change its XML representation.

Reordering and skiping attributes

Dynamic Types macros expand some useful methods that allow simplifying the customization.

For each dynamic attribute named XXX, dynamic type macros expand the methods:

 void ConcreteDT::StoreXXX(Storage & s);
 void ConcreteDT::LoadXXX(Storage & s);

Using such methods you can easily store/load a concrete dynamic attribute separately. Be careful, LoadXXX requires the attribute XXX to be instantiated before calling it and it will mark it automatically as removed if the attribute is not present in the XML file. It is important to store the attributes in the same order you load them.

The following example will store and load its attributes in the inverse order to the default one, and skips the third attribute (MyDynType).

        void ConcreteDT::StoreOn(CLAM::Storage & storage)
        {
                StoreMyInt(storage);
                StoreMyList(storage);
                // MyDynType is not stored
                StoreMyArray(storage);
                StoreMyDummyComponent(storage);
        }
        void ConcreteDT::LoadFrom(CLAM::Storage & storage)
        {
                // First of all asure that all attributes are instantiated
                AddAll()
                UpdateData();
                // Then load them
                LoadMyInt(storage);
                LoadMyList(storage);
                // MyDynType is not loaded
                LoadMyArray(storage);
                LoadMyDummyComponent(storage);
        }

Recalling the default implementation

StoreAllDynAttributes() and LoadAllDynamicAttributes() are another macro expanded methods. They are called from the default StoreOn and LoadFrom implementation. So, by calling them we can reproduce them and it is easy to add non dynamic subparts before or after them or forcing some attributes to be or not present before them. The first step of LoadAllDynamicAttributes() is to instantiate all the dynamic attributes that will be marked as erased if they are not in the XML document.

Adding content not from dynamic attributes

If you simply want to add a non dynamic attribute to the XML representation, you may call those expanded functions and then using a suited XML adapter for the attribute and store it. Refer on how to define the XML format for a normal (non DynamicType) Component to know about those adaptators and how they are used.

The following example stores two extra items on the XML. An existing member of the class (mExtraNonDynamicAttribute) and a literal string as an XML attribute (the false value).

void ConcreteDT::StoreOn(CLAM::Storage & storage)s
{
        // Store a temporary object in the first place
        CLAM::XMLAdapter<char*> adapter1("Addedcontent", "Added", false);
        storage.Store(&adapter1);

        // Call the default implementation
        StoreAllDynAttributes();

        // Store a non dynamic attribute member
        CLAM::XMLComponentAdapter adapter2(mExtraNonDynamicAttribute,
                "ExtraNonDynamic", true);
        storage.Store(&adapter2);
}

void ConcreteDT::LoadFrom(CLAM::Storage & storage)
{
        // std::string is not vulnerable to buffer overflows on loading
        std::string foo; // A temp
        CLAM::XMLAdapter<std::string> adapter1(foo, "Added", false);
        storage.Load(&adapter1);

        LoadAllDynAttributes();

        CLAM::XMLComponentAdapter adapter2(mExtraNonDynamicAttribute,
                , "ExtraNonDynamic", true);
        storage.Load(&adapter2);
}

Storing not as XML elements or changing the tag name

Of course, we can also use Adapters with the dynamic attributes instead of using StoreXXX and LoadXXX. This is useful to store a dynamic attribute as XML attribute or XML plain content or to change the name from the one the attribute has. Again, refer to the XML developer guide.

When using adapters with dynamic attributes you must take care of some dynamic attributes tasks:

When storing a dynamic attribute XXX you must check that it is instantiated using the function HasXXX. When loading you must check that the Storage::Load returns true. When it returns false it is advisable to mark it as removed.

void ConcreteDT::StoreOn(CLAM::Storage & storage)
{
        StoreMyDummyComponent(storage);
        StoreMyArray(storage);
        StoreMyDynType(storage);
        StoreMyList(storage);
        // MyInt is stored as an attribute (the default is element
        // and with a different name ('Size').
        if (HasMyInt())
        {
                CLAM::XMLAdapter<int> adapter(GetMyInt(), "Size", false);
                storage.Store(&adapter);
        }
}
void ConcreteDT::LoadFrom(CLAM::Storage & storage)
{
        // First of all asure that all attributes are instantiated
        AddAll()
        UpdateData();
        // Then load them
        LoadMyDummyComponent(storage);
        LoadMyArray(storage);
        LoadMyDynType(storage);
        LoadMyList(storage);
        // MyInt is loaded as an attribute (the default is element
        // and with a different name ('Size').
        CLAM::XMLAdapter<int> adapter(GetMyInt(), "Size", false);
        if (!storage.Load(&adapter))
        {
                RemoveMyInt();
        }
}

Keeping several alternative XML formats

Normally you will define the storage customization on the same concrete dynamic type class. But sometimes, you want to keep the default implementation o several customized implementations. A good way of doing this is by subclassing the concrete Dynamic Type and redefining the storage related methods as above but in the subclasses.

Author:
David Garcia.
Generated by  doxygen 1.6.3