
I think it would be no exaggeration to say that almost every information system developer is faced with the task of generating initial data during implementation.
Caché developers have several standard approaches to initializing initial data:
- loading data for reference classes from external files,
- getting data from online services,
- import of static data from global files,
- executing class methods that create the initial data from the “wired” data code.
For initialization of static data, small reference books or any system configuration data, there is another way that will be discussed in the article.
In Caché, it is possible to include XML data blocks in class code — XDATA blocks. Usually this data is used to store, together with a class, data on forms of
Zen pages and
Zen reports . In these blocks, you can also perfectly store initial data for persistent classes.
XData deserialization
Consider an example of a simple stored class with one property. This will be a class of regions with names - a typical example of a directory.
')
The code for this class in Caché is as follows:
Class map.Region Extends% Persistent
{
/// Name
Property Name As% String ;
}Add the initial data to the class in XML format in the XData block:
XData populate
{
< xml >
< item >
< Name > Krasnoyarsk Territory </ Name >
</ item >
< item >
< Name > Sverdlovsk Region </ Name >
</ item >
< item >
< Name > Khabarovsk Territory </ Name >
</ item >
</ xml >
}In order to load this data from the class, add the class method Populate. In addition, to work with XML data, you need to make the class
XML-enabled — we also add the% XML.Adaptor class to the inheritance list. As a result, the converted class code looks like this:
Class map.Region Extends ( % Persistent , % XML.Adaptor )
{
/// Name
Property Name As% String ;
ClassMethod Populate () As% Status
{
#dim sc As % Status = $$$ OK
// clear existing class data
s sc = .. % DeleteExtent ()
if $$$ ISERR (sc) quit sc
// load xml-data of the XData block from the library of compiled classes
#dim stream As % Stream.Object = ## class ( % Dictionary.CompiledXData ). % OpenId (.. % ClassName (1) _ "||" _ "populate" ). Data
// create an XML reader instance
#dim reader As % XML.Reader = ## class ( % XML.Reader ). % New ()
// open the xml stream in the reader
set sc = reader. OpenStream (stream, "literal" )
if $$$ ISERR (sc) quit sc
// instructions to the reader in which element to look for class data
do reader. Correlate ( "item" , .. % ClassName (1))
#dim obj as % Persistent
// load in the cycle of class elements
while reader. Next (.obj, .sc)
{
if $$$ ISERR (sc) quit
// save read from xml instance to database
set sc = obj. % Save ()
if $$$ ISERR (sc) quit
set obj = ""
}
quit sc
}
XData populate
{
< xml >
< item >
< Name > Krasnoyarsk Territory </ Name >
</ item >
< item >
< Name > Sverdlovsk Region </ Name >
</ item >
< item >
< Name > Khabarovsk Territory </ Name >
</ item >
</ xml >
}
}As can be seen from the code, all the work is done by the class% XML.Reader, which allows you to read the “object” data from XML.
To load data into a class during deployment, it suffices to execute the Populate class method:
w ## class ( map.Region ). Populate ()Make sure the class instances are actually created. Run the query in the SQL shell of the terminal:
XMLDEPLOY>d $System.SQL.Shell() SQL Command Line Shell ---------------------------------------------------- The command prefix is currently set to: <<nothing>>. Enter q to quit, ? for help. XMLDEPLOY>>select * from map.Region 1. select * from map.Region ID Name 1 2 3 3 Rows(s) Affected statement prepare time: 0.9125s, elapsed execute time: 0.0687s. --------------------------------------------------------------------------- XMLDEPLOY>>quit XMLDEPLOY>
Filling XData
Obviously, the XData block can be filled “manually”. And this is convenient if there are few objects in the class. But if there are many, the program can do it.
We use the% XML.Writer class, which allows us to serialize the class instances into XML.
To do this, add the serialization method to the class:
ClassMethod SerializeToFile ( file as% String ) as% Status
{
#dim sc as % Status
#dim wr as % XML.Writer
set wr = ## class ( % XML.Writer ). % New ()
// set the file as output device
set sc = wr. OutputToFile (file) if $$$ ISERR (sc) quit sc
// open root tag
set sc = wr. RootElement ( "xml" ) if $$$ ISERR (sc) quit sc
#dim rset as % ResultSet
// execute an Extent query containing all objects of the class
set rset = ## class ( % ResultSet ). % New ( "map.Region: Extent" )
do rset. Execute ()
#dim obj
while (rset. Next ()) {
set obj = ## class ( map.Region ). % OpenId (rset. Data ( "ID" ))
// serialize the object
set sc = wr. Object (obj)
if $$$ ISERR (sc) quit
}
if $$$ ISERR (sc) quit sc
// close root tag
set sc = wr. EndRootElement ()
quit sc
}The method prints to the file all class objects serialized in XML.
Perform the method in the terminal:
XMLDEPLOY>D $System.OBJ.DisplayError(
And then open the file with any editor / viewer:

The resulting XML is already easy to insert into the XData block.
Total
Similarly, all classes that require input of initial data during deployment can be provided with similar methods. As a result, with the help of the above technique, the initial data is stored together with the class for which they are needed, in a technological XML format. The client can only be supplied with class code, and the generation of initial data can be performed using the Populate method. And what if the classes are related to each other? This and other scenarios will be discussed in the second part of the article. To be continued…