Skip to content

InstantXML ​

File-based XML persistence broker for InstantObjects.

Overview ​

The InstantXML unit provides a file-based persistence broker that stores business objects as XML files in the file system. Each object is saved as a separate XML file with a structured naming convention, organized into folders by class/storage name.

This broker is ideal for:

  • Small to medium datasets
  • Prototyping and development
  • Portable applications without database dependencies
  • Data exchange and archival scenarios
  • Offline/disconnected applications

File Structure:

RootFolder/
  ├─ Company/
  │   ├─ TCompany.ABC123.1.xml
  │   ├─ TCompany.DEF456.1.xml
  │   └─ TCompany.GHI789.2.xml
  ├─ Contact/
  │   ├─ TContact.001.1.xml
  │   └─ TContact.002.1.xml
  └─ Product/
      └─ TProduct.PROD001.1.xml

File Naming Convention: ClassName.ObjectId.UpdateCount.xml

Key Classes ​

TXMLFilesAccessor ​

Low-level connection component that manages XML file operations.

Inheritance:

TComponent → TCustomConnection → TXMLFilesAccessor

Key Features:

  • Creates and manages folder structure
  • Reads/writes objects to XML files
  • Supports custom file naming via override
  • UTF-8 or ISO-8859-1 encoding options
  • Custom load/save events for redirection

Properties ​

PropertyTypeDefaultDescription
RootFolderstring''Root directory for XML file storage
XMLFileFormatTXMLFileFormatxffUtf8File encoding (xffUtf8 or xffIso)
OnCustomLoadXMLFileTXMLFileOpenEventnilCustom file loading handler
OnCustomSaveToXMLFileTXMLFileSaveEventnilCustom file saving handler

Methods ​

GetObjectFileName ​
pascal
function GetObjectFileName(const AStorageName, AObjectClassName,
  AObjectId: string): string; virtual;

Returns the full file path for an object. Default implementation:

pascal
Result := RootFolder + AStorageName + PathDelim + AObjectClassName + '.'
  + AObjectId + '.1' + DOT_XML_EXT;
// Example: C:\Data\Company\TCompany.ABC123.1.xml

Override this method to customize file naming or storage location.

ReadInstantObject ​
pascal
function ReadInstantObject(const AObject: TInstantObject;
  const AStorageName, AObjectId: string;
  out AObjectUpdateCount: Integer): Boolean;

Loads object from XML file. Returns True if successful.

WriteInstantObject ​
pascal
function WriteInstantObject(const AObject: TInstantObject;
  const AStorageName: string;
  out AObjectUpdateCount: Integer): Boolean;

Saves object to XML file. Returns True if successful.

DeleteInstantObject ​
pascal
function DeleteInstantObject(const AObject: TInstantObject;
  const AStorageName: string): Boolean;

Deletes object's XML file. Returns True if successful.

LocateInstantObject ​
pascal
function LocateInstantObject(const AStorageName, AObjectClassName,
  AObjectId: string): Boolean;

Checks if object file exists. Returns True if found.

LoadFileList ​
pascal
procedure LoadFileList(const AFileList: TStringList;
  const AStorageNames: TStrings;
  const AFilterWildCard: string = '');

Loads list of XML files from specified storage folders.

Parameters:

  • AFileList: Receives list of filenames
  • AStorageNames: List of folders to search
  • AFilterWildCard: Optional wildcard filter (e.g., 'TCompany*.xml')

TInstantXMLConnectionDef ​

Connection definition for XML broker.

Properties ​

PropertyTypeDescription
RootFolderstringRoot folder for XML files
XMLFileFormatTXMLFileFormatUTF-8 or ISO encoding

TInstantXMLConnector ​

Main connector component for XML file persistence.

Inheritance:

TComponent → TInstantConnector → TInstantConnectionBasedConnector → TInstantXMLConnector

Properties ​

PropertyTypeDefaultDescription
ConnectionTXMLFilesAccessornilXML files accessor component
UseTransactionsBooleanFalseTransactions not supported (always False)
LoginPromptBooleanFalseNo login required
UseUnicodeBooleanFalseUnicode support (deprecated)
BlobStreamFormatTInstantStreamFormatsfXMLAlways XML format
ReadObjectListWithNoLockBooleanTrueNo locking mechanism

Methods ​

InternalBuildDatabase ​
pascal
procedure InternalBuildDatabase(Scheme: TInstantScheme); override;

Creates root folder if it doesn't exist. Does not create class-specific folders (created on demand when objects are written).

TInstantXMLBroker ​

Broker that manages resolvers for XML persistence.

Inheritance:

TInstantBroker → TInstantCustomRelationalBroker → TInstantXMLBroker

Properties ​

PropertyTypeDescription
ConnectorTInstantXMLConnectorParent connector
ResolverCountIntegerNumber of registered resolvers
Resolvers[Index]TInstantXMLResolverResolver by index

Methods ​

FindResolver ​
pascal
function FindResolver(const StorageName: string): TInstantXMLResolver;

Finds existing resolver for a storage name (folder).

CreateResolver ​
pascal
function CreateResolver(const StorageName: string): TInstantXMLResolver;

Creates a new resolver for a storage name.

EnsureResolver ​
pascal
function EnsureResolver(Map: TInstantAttributeMap): TInstantCustomResolver; override;

Returns existing resolver or creates a new one for the attribute map's storage.

TInstantXMLResolver ​

Handles object persistence operations for a specific storage (folder).

Inheritance:

TInstantCustomResolver → TInstantXMLResolver

Properties ​

PropertyTypeDescription
BrokerTInstantXMLBrokerParent broker
StorageNamestringStorage folder name

Methods ​

InternalStoreMap ​
pascal
procedure InternalStoreMap(AObject: TInstantObject;
  Map: TInstantAttributeMap;
  ConflictAction: TInstantConflictAction;
  Info: PInstantOperationInfo); override;

Saves object to XML file. Handles ID changes by deleting old file.

InternalRetrieveMap ​
pascal
procedure InternalRetrieveMap(AObject: TInstantObject;
  const AObjectId: string; Map: TInstantAttributeMap;
  ConflictAction: TInstantConflictAction;
  Info: PInstantOperationInfo;
  const AObjectData: TInstantAbstractObjectData); override;

Loads object from XML file.

InternalDisposeMap ​
pascal
procedure InternalDisposeMap(AObject: TInstantObject;
  Map: TInstantAttributeMap;
  ConflictAction: TInstantConflictAction;
  Info: PInstantOperationInfo); override;

Deletes object's XML file.

TInstantXMLQuery ​

Query implementation that loads and filters XML files in memory.

Inheritance:

TInstantQuery → TInstantCustomRelationalQuery → TInstantXMLQuery

How it works:

  1. Loads all XML files from specified storage folders
  2. Filters by class name
  3. Creates object references for matching files
  4. Applies IQL WHERE filter (in memory)
  5. Sorts by ORDER BY clause (QuickSort)
  6. Returns result set

Properties ​

PropertyTypeDescription
StorageNamesTStringListFolders to search (populated by translator)
ObjectClassNamesTStringListClasses to include (populated by translator)
FilterWildCardstringFilename wildcard filter
ObjectReferenceCountIntegerNumber of matching objects
ObjectReferences[Index]TInstantObjectReferenceObject reference by index

Methods ​

InternalOpen ​
pascal
procedure InternalOpen; override;

Opens query:

  1. Parses IQL command
  2. Loads file list from storage folders
  3. Creates object references
  4. Sorts if ORDER BY present
SortObjectReferences ​
pascal
procedure SortObjectReferences;

Sorts object references using QuickSort based on ORDER BY clause.

TInstantXMLTranslator ​

IQL to XML query translator.

Inheritance:

TInstantRelationalTranslator → TInstantXMLTranslator

Methods ​

TranslateClassRef ​
pascal
function TranslateClassRef(ClassRef: TInstantIQLClassRef;
  Writer: TInstantIQLWriter): Boolean; override;

Translates IQL class reference to storage/class name lists. Handles inheritance (ANY keyword).

Constants ​

pascal
const
  XML_UTF8_HEADER = '<?xml version="1.0" encoding="UTF-8"?>';
  XML_ISO_HEADER = '<?xml version="1.0" encoding="ISO-8859-1"?>';
  XML_EXT = 'xml';
  DOT_XML_EXT = '.xml';
  XML_WILDCARD = '*.xml';

Types ​

TXMLFileFormat ​

pascal
type
  TXMLFileFormat = (xffUtf8, xffIso);

File encoding format:

  • xffUtf8: UTF-8 encoding (default, recommended)
  • xffIso: ISO-8859-1 encoding (for legacy compatibility)

Event Types ​

pascal
type
  TXMLFileOpenEvent = function(const AObject: TInstantObject;
    const AObjectId, AFileName: string): Boolean of Object;

  TXMLFileSaveEvent = function(const AObject: TInstantObject;
    const AFileName: string): Boolean of Object;

Usage Patterns ​

Basic Setup ​

Configure XML connector:

pascal
uses
  InstantXML;

var
  XMLConnection: TXMLFilesAccessor;
  XMLConnector: TInstantXMLConnector;
begin
  // Create connection
  XMLConnection := TXMLFilesAccessor.Create(Self);
  XMLConnection.RootFolder := 'C:\MyApp\Data';
  XMLConnection.XMLFileFormat := xffUtf8;

  // Create connector
  XMLConnector := TInstantXMLConnector.Create(Self);
  XMLConnector.Connection := XMLConnection;
  XMLConnector.Connected := True;
end;

Design-Time Setup ​

  1. Drop TXMLFilesAccessor on form
  2. Set RootFolder property
  3. Drop TInstantXMLConnector on form
  4. Set Connection property to TXMLFilesAccessor
  5. Set Connected := True

Programmatic Connection ​

pascal
procedure ConnectToXMLDatabase(const DataFolder: string);
var
  Connector: TInstantXMLConnector;
begin
  Connector := TInstantXMLConnector.Create(nil);
  try
    Connector.IsDefault := True;
    Connector.BrokerClass := TInstantXMLBroker;

    // Connection string format: RootFolder
    Connector.ConnectionDef := TInstantXMLConnectionDef.Create(
      'XML;' + DataFolder, True);

    Connector.Connected := True;
    ShowMessage('Connected to XML database at: ' + DataFolder);
  except
    Connector.Free;
    raise;
  end;
end;

Storing and Retrieving Objects ​

Objects are automatically stored as XML files:

pascal
var
  Company: TCompany;
begin
  // Create and store
  Company := TCompany.Create(XMLConnector);
  try
    Company.Id := 'ABC123';
    Company.Name := 'Acme Corporation';
    Company.Store;
    // Creates: C:\MyApp\Data\Company\TCompany.ABC123.1.xml
  finally
    Company.Free;
  end;

  // Retrieve
  Company := TCompany.Retrieve('ABC123', False, False, XMLConnector);
  try
    ShowMessage(Company.Name);  // 'Acme Corporation'
  finally
    Company.Free;
  end;
end;

Querying XML Files ​

Use IQL to query objects:

pascal
var
  Query: TInstantQuery;
begin
  Query := TInstantQuery.Create(nil);
  try
    Query.Connector := XMLConnector;
    Query.Command := 'SELECT * FROM TCompany WHERE Name LIKE ''%Corp%''';
    Query.Open;

    while not Query.EOF do
    begin
      ShowMessage(Query.CurrentObject.ToString);
      Query.Next;
    end;
  finally
    Query.Free;
  end;
end;

Custom File Naming ​

Override GetObjectFileName to customize file names:

pascal
type
  TMyXMLFilesAccessor = class(TXMLFilesAccessor)
  public
    function GetObjectFileName(const AStorageName, AObjectClassName,
      AObjectId: string): string; override;
  end;

function TMyXMLFilesAccessor.GetObjectFileName(const AStorageName,
  AObjectClassName, AObjectId: string): string;
begin
  // Custom naming: Year\Month\ClassName_Id.xml
  Result := RootFolder +
            FormatDateTime('yyyy\mm\', Now) +
            AObjectClassName + '_' + AObjectId + DOT_XML_EXT;
  // Example: C:\Data\2025\01\TCompany_ABC123.xml
end;

Custom Storage Location ​

Redirect storage to database or cloud:

pascal
type
  TDatabaseXMLAccessor = class(TXMLFilesAccessor)
  protected
    function InternalReadInstantObject(const AObject: TInstantObject;
      const AStorageName, AObjectId: string;
      out AObjectUpdateCount: Integer): Boolean; override;
    function InternalWriteInstantObject(const AObject: TInstantObject;
      const AStorageName: string;
      out AObjectUpdateCount: Integer): Boolean; override;
  end;

function TDatabaseXMLAccessor.InternalReadInstantObject(
  const AObject: TInstantObject; const AStorageName, AObjectId: string;
  out AObjectUpdateCount: Integer): Boolean;
var
  XMLContent: string;
  Stream: TStringStream;
begin
  // Load XML from database
  XMLContent := LoadXMLFromDatabase(AStorageName, AObjectId);

  Stream := TStringStream.Create(XMLContent, TEncoding.UTF8);
  try
    InstantReadObject(Stream, sfXML, AObject);
    Result := True;
    AObjectUpdateCount := 1;
  finally
    Stream.Free;
  end;
end;

function TDatabaseXMLAccessor.InternalWriteInstantObject(
  const AObject: TInstantObject; const AStorageName: string;
  out AObjectUpdateCount: Integer): Boolean;
var
  Stream: TStringStream;
begin
  Stream := TStringStream.Create('', TEncoding.UTF8);
  try
    InstantWriteObject(Stream, sfXML, AObject);

    // Save XML to database
    SaveXMLToDatabase(AStorageName, AObject.Id, Stream.DataString);
    Result := True;
    AObjectUpdateCount := 1;
  finally
    Stream.Free;
  end;
end;

Custom Load/Save Events ​

Use events for custom processing:

pascal
procedure TMainForm.XMLConnectionCustomLoadXMLFile(const AObject: TInstantObject;
  const AObjectId, AFileName: string): Boolean;
begin
  // Custom loading logic
  if FileExists(AFileName) then
  begin
    DecryptFile(AFileName);  // Decrypt before loading
    Result := False;  // Let default handler load decrypted file
  end
  else
    Result := False;
end;

procedure TMainForm.XMLConnectionCustomSaveToXMLFile(const AObject: TInstantObject;
  const AFileName: string): Boolean;
var
  Stream: TStringStream;
begin
  // Custom saving logic
  Stream := TStringStream.Create('', TEncoding.UTF8);
  try
    InstantWriteObject(Stream, sfXML, AObject);
    Stream.SaveToFile(AFileName);
    EncryptFile(AFileName);  // Encrypt after saving
    Result := True;
  finally
    Stream.Free;
  end;
end;

// Attach events
XMLConnection.OnCustomLoadXMLFile := XMLConnectionCustomLoadXMLFile;
XMLConnection.OnCustomSaveToXMLFile := XMLConnectionCustomSaveToXMLFile;

Wildcard Filtering ​

Filter files by wildcard pattern:

pascal
var
  Query: TInstantXMLQuery;
begin
  Query := XMLConnector.CreateQuery as TInstantXMLQuery;
  try
    Query.Command := 'SELECT * FROM TCompany';
    Query.FilterWildCard := 'TCompany.ABC*.xml';  // Only load ABC* IDs
    Query.Open;

    // Process filtered results
  finally
    Query.Free;
  end;
end;

Folder Organization ​

Organize by date or category:

pascal
type
  TDateOrganizedAccessor = class(TXMLFilesAccessor)
  public
    function GetObjectFileName(const AStorageName, AObjectClassName,
      AObjectId: string): string; override;
  end;

function TDateOrganizedAccessor.GetObjectFileName(const AStorageName,
  AObjectClassName, AObjectId: string): string;
var
  YearMonth: string;
begin
  YearMonth := FormatDateTime('yyyy-mm', Now);
  Result := RootFolder + AStorageName + PathDelim + YearMonth + PathDelim +
            AObjectClassName + '.' + AObjectId + '.1' + DOT_XML_EXT;
  // Example: C:\Data\Company\2025-01\TCompany.ABC123.1.xml
end;

Building XML Database ​

Create folder structure:

pascal
procedure BuildXMLDatabase(const RootFolder: string; Model: TInstantModel);
var
  Connector: TInstantXMLConnector;
  Scheme: TInstantScheme;
begin
  Connector := TInstantXMLConnector.Create(nil);
  try
    Connector.Connection := TXMLFilesAccessor.Create(nil);
    Connector.Connection.RootFolder := RootFolder;

    Scheme := Connector.CreateScheme(Model);
    try
      Connector.BuildDatabase(Scheme);
      // Creates root folder only
      // Class folders created on first object write
    finally
      Scheme.Free;
    end;
  finally
    Connector.Free;
  end;
end;

Batch Import ​

Import multiple objects:

pascal
procedure ImportObjectsFromXML(const SourceFolder: string;
  Connector: TInstantXMLConnector);
var
  FileList: TStringList;
  I: Integer;
  FileName, ClassName, ObjectId: string;
  Obj: TInstantObject;
  FileStream: TFileStream;
begin
  FileList := TStringList.Create;
  try
    // Load all XML files
    GlobalLoadFileList(SourceFolder, FileList);

    for I := 0 to FileList.Count - 1 do
    begin
      FileName := SourceFolder + PathDelim + FileList[I];
      ClassName := GetFileClassName(FileList[I]);
      ObjectId := GetFileId(FileList[I]);

      // Create object instance
      Obj := InstantFindClass(ClassName).Create(Connector) as TInstantObject;
      try
        // Load from XML
        FileStream := TFileStream.Create(FileName, fmOpenRead);
        try
          InstantReadObject(FileStream, sfXML, Obj);
        finally
          FileStream.Free;
        end;

        // Store to database
        Obj.Store;
      finally
        Obj.Free;
      end;
    end;
  finally
    FileList.Free;
  end;
end;

Backup and Export ​

Export database to ZIP archive:

pascal
uses
  System.Zip;

procedure BackupXMLDatabase(const RootFolder, BackupFile: string);
var
  ZipFile: TZipFile;
begin
  ZipFile := TZipFile.Create;
  try
    ZipFile.Open(BackupFile, zmWrite);
    ZipFile.Add(RootFolder, '');  // Add entire folder
    ZipFile.Close;
    ShowMessage('Backup created: ' + BackupFile);
  finally
    ZipFile.Free;
  end;
end;

procedure RestoreXMLDatabase(const BackupFile, TargetFolder: string);
var
  ZipFile: TZipFile;
begin
  ZipFile := TZipFile.Create;
  try
    ZipFile.Open(BackupFile, zmRead);
    ZipFile.ExtractAll(TargetFolder);
    ZipFile.Close;
    ShowMessage('Database restored to: ' + TargetFolder);
  finally
    ZipFile.Free;
  end;
end;

File Naming Convention ​

Standard Format ​

ClassName.ObjectId.UpdateCount.xml

Components:

  • ClassName: Business object class name (e.g., TCompany)
  • ObjectId: Unique object identifier (e.g., ABC123)
  • UpdateCount: Object version number (currently always 1)
  • Extension: Always .xml

Examples:

TCompany.ABC123.1.xml
TContact.001.1.xml
TProduct.PROD_2025_001.1.xml

Parsing File Names ​

Helper functions to extract information:

pascal
function GetFileClassName(const FileName: string): string;
// Returns: 'TCompany' from 'TCompany.ABC123.1.xml'

function GetFileId(const FileName: string): string;
// Returns: 'ABC123' from 'TCompany.ABC123.1.xml'

function GetObjectUpdateCount(const FileName: string): Integer;
// Returns: 1 from 'TCompany.ABC123.1.xml'

XML File Format ​

Example XML File ​

File: TCompany.ABC123.1.xml

xml
<?xml version="1.0" encoding="UTF-8"?>
<TCompany>
  <Class>TCompany</Class>
  <Id>ABC123</Id>
  <UpdateCount>1</UpdateCount>
  <Name>Acme Corporation</Name>
  <Address>123 Main St</Address>
  <City>Springfield</City>
  <Employees>
    <TEmployee>
      <Class>TEmployee</Class>
      <Id>EMP001</Id>
    </TEmployee>
    <TEmployee>
      <Class>TEmployee</Class>
      <Id>EMP002</Id>
    </TEmployee>
  </Employees>
</TCompany>

Format characteristics:

  • UTF-8 or ISO-8859-1 encoding
  • InstantObjects standard XML format (see InstantClasses)
  • Embedded objects included inline
  • References stored as class/ID pairs
  • Parts stored as nested XML

Database Building ​

TInstantDBBuildXMLCommand ​

Base class for XML database build commands.

Properties ​

PropertyTypeDescription
ConnectorTInstantXMLConnectorParent connector
BrokerTInstantXMLBrokerParent broker

TInstantDBBuildXMLAddTableCommand ​

Creates storage folder for a class.

pascal
procedure InternalExecute; override;

Execution:

  1. Checks root folder exists
  2. Creates root folder if needed
  3. Does NOT create class folder (created on first write)

TInstantDBBuildXMLDropTableCommand ​

Deletes storage folder and all XML files.

pascal
procedure InternalExecute; override;

Execution:

  1. Locates class folder
  2. Deletes all XML files in folder
  3. Removes folder

Example:

pascal
var
  Builder: TInstantDBBuilder;
  Connector: TInstantXMLConnector;
begin
  Builder := TInstantDBBuilder.Create(nil);
  try
    Builder.Connector := Connector;
    Builder.TargetModel := InstantModel1.CurrentModel;
    Builder.BuildCommandSequence;
    Builder.CommandSequence.Execute;
    // Creates root folder structure
  finally
    Builder.Free;
  end;
end;

Performance Considerations ​

Strengths ​

  1. Simplicity: No database server required
  2. Portability: Copy folder = backup/restore
  3. Human-Readable: XML files can be edited manually
  4. Version Control: Individual files work well with Git/SVN
  5. No Installation: Zero configuration

Limitations ​

  1. Query Performance: All files loaded into memory

    • Small datasets (< 10,000 objects): Good performance
    • Large datasets (> 100,000 objects): Poor performance
  2. No Transactions: File-based = no ACID guarantees

  3. No Concurrency Control: File locking only

  4. No Server-Side Filtering: WHERE clause applied in memory

  5. No Indexes: Linear scan of files

Optimization Tips ​

1. Use Wildcard Filtering ​

pascal
Query.FilterWildCard := 'TCompany.2025*.xml';  // Load only 2025 data

2. Organize by Folders ​

pascal
// Override GetObjectFileName to organize by year/month
// Limits files scanned per query

3. Limit Result Sets ​

pascal
// Use TOP or FIRST in IQL
Query.Command := 'SELECT FIRST 100 * FROM TCompany ORDER BY Name';

4. Use Specific Classes ​

pascal
// Don't use ANY keyword for large hierarchies
Query.Command := 'SELECT * FROM TCompany';  // Good
Query.Command := 'SELECT * FROM ANY TCompany';  // Bad (loads all subclasses)

5. Retrieve by ID ​

pascal
// Direct retrieval bypasses query
Company := TCompany.Retrieve('ABC123', False, False, Connector);

Transaction Support ​

Important: XML broker does NOT support real transactions.

pascal
procedure TInstantXMLConnector.InternalStartTransaction;
begin
  { TODO: Start transaction for Connection }
end;

procedure TInstantXMLConnector.InternalCommitTransaction;
begin
  { TODO: Commit transaction for Connection }
end;

procedure TInstantXMLConnector.InternalRollbackTransaction;
begin
  { TODO: Roll back transaction for Connection }
end;

Workaround for transactional behavior:

pascal
procedure SaveWithBackup(Obj: TInstantObject);
var
  BackupFile: string;
begin
  // Manual "transaction" via backup
  BackupFile := GetObjectFileName(Obj) + '.bak';

  if FileExists(GetObjectFileName(Obj)) then
    CopyFile(GetObjectFileName(Obj), BackupFile);

  try
    Obj.Store;
    DeleteFile(BackupFile);  // "Commit"
  except
    if FileExists(BackupFile) then
    begin
      CopyFile(BackupFile, GetObjectFileName(Obj));  // "Rollback"
      DeleteFile(BackupFile);
    end;
    raise;
  end;
end;

Best Practices ​

1. Use for Appropriate Scenarios ​

✓ Good use cases:

  • Prototypes and demos
  • Small applications (< 10,000 objects)
  • Portable applications
  • Configuration storage
  • Offline/disconnected scenarios

✗ Poor use cases:

  • Large datasets (> 100,000 objects)
  • Multi-user applications
  • High-concurrency scenarios
  • Complex queries

2. Implement Regular Backups ​

pascal
// Schedule regular backups
procedure BackupXMLData;
var
  BackupFolder: string;
begin
  BackupFolder := Format('Backup_%s', [FormatDateTime('yyyymmdd_hhnnss', Now)]);
  CopyDirectory(XMLConnection.RootFolder, BackupFolder);
end;

3. Validate XML Files ​

pascal
// Validate XML integrity on startup
procedure ValidateXMLDatabase(const RootFolder: string);
var
  FileList: TStringList;
  I: Integer;
  XMLDoc: IXMLDocument;
begin
  FileList := TStringList.Create;
  try
    GlobalLoadFileList(RootFolder, FileList);
    for I := 0 to FileList.Count - 1 do
    begin
      try
        // Attempt to parse XML
        XMLDoc := LoadXMLDocument(FileList[I]);
      except
        on E: Exception do
          LogError('Corrupt XML file: ' + FileList[I]);
      end;
    end;
  finally
    FileList.Free;
  end;
end;

4. Use UTF-8 Encoding ​

pascal
// Always use UTF-8 for international characters
XMLConnection.XMLFileFormat := xffUtf8;

5. Implement File Locking ​

pascal
// Use exclusive access when modifying
Stream := TFileStream.Create(FileName,
  fmOpenReadWrite or fmShareExclusive);

6. Monitor Folder Size ​

pascal
// Implement folder size monitoring
procedure CheckDatabaseSize(const RootFolder: string);
var
  Size: Int64;
begin
  Size := GetFolderSize(RootFolder);
  if Size > 100 * 1024 * 1024 then  // 100 MB
    ShowWarning('Database size exceeds 100 MB. Consider migrating to SQL.');
end;

Troubleshooting ​

Files Not Found ​

Problem: LocateInstantObject returns False.

Diagnosis:

pascal
// Check file existence
FileName := XMLConnection.GetObjectFileName('Company', 'TCompany', 'ABC123');
if not FileExists(FileName) then
  ShowMessage('File not found: ' + FileName);

Solutions:

  • Verify RootFolder is correct
  • Check folder permissions
  • Verify object was stored successfully

Corrupt XML Files ​

Problem: Exception when loading objects.

Diagnosis:

pascal
// Try loading XML manually
var
  XMLDoc: IXMLDocument;
begin
  try
    XMLDoc := LoadXMLDocument(FileName);
  except
    on E: Exception do
      ShowMessage('XML Error: ' + E.Message);
  end;
end;

Solutions:

  • Restore from backup
  • Manually edit XML to fix syntax
  • Delete and recreate object

Slow Query Performance ​

Problem: Queries take long time.

Diagnosis:

pascal
// Count files being loaded
FileCount := GetFileCount(XMLConnection.RootFolder);
ShowMessage(Format('Loading %d files', [FileCount]));

Solutions:

  • Use wildcard filtering
  • Organize into subfolders
  • Limit result sets with FIRST/TOP
  • Consider migrating to SQL broker

Encoding Issues ​

Problem: Special characters corrupted.

Solution:

pascal
// Ensure UTF-8 encoding
XMLConnection.XMLFileFormat := xffUtf8;

// Verify file encoding externally

Concurrency Conflicts ​

Problem: Multiple users overwriting changes.

Solution:

pascal
// Implement application-level locking
// Or migrate to SQL broker with real transaction support

Migration from/to XML Broker ​

Export from SQL to XML ​

pascal
procedure ExportToXML(SourceConnector, TargetXMLConnector: TInstantConnector);
var
  Query: TInstantQuery;
  Obj, NewObj: TInstantObject;
begin
  Query := SourceConnector.CreateQuery;
  try
    Query.Command := 'SELECT * FROM ANY TInstantObject';
    Query.Open;

    while not Query.EOF do
    begin
      Obj := Query.CurrentObject as TInstantObject;

      // Create copy in XML database
      NewObj := TInstantObjectClass(Obj.ClassType).Create(TargetXMLConnector);
      try
        NewObj.Assign(Obj);
        NewObj.Store;
      finally
        NewObj.Free;
      end;

      Query.Next;
    end;
  finally
    Query.Free;
  end;
end;

Import from XML to SQL ​

pascal
procedure ImportFromXML(SourceXMLConnector, TargetConnector: TInstantConnector);
var
  FileList: TStringList;
  I: Integer;
  ClassName, ObjectId: string;
  Obj, NewObj: TInstantObject;
begin
  FileList := TStringList.Create;
  try
    (SourceXMLConnector.Connection as TXMLFilesAccessor).LoadFileList(
      FileList, GetAllStorageNames);

    for I := 0 to FileList.Count - 1 do
    begin
      ClassName := GetFileClassName(FileList[I]);
      ObjectId := GetFileId(FileList[I]);

      // Load from XML
      Obj := InstantFindClass(ClassName).Create(SourceXMLConnector);
      try
        Obj.Id := ObjectId;
        Obj.Refresh;

        // Store to SQL
        NewObj := TInstantObjectClass(Obj.ClassType).Create(TargetConnector);
        try
          NewObj.Assign(Obj);
          NewObj.Store;
        finally
          NewObj.Free;
        end;
      finally
        Obj.Free;
      end;
    end;
  finally
    FileList.Free;
  end;
end;

See Also ​

Source Code ​

File: InstantXML.pasLocation: Source/Brokers/XML/

Summary ​

The InstantXML broker provides file-based XML persistence ideal for small applications, prototyping, and portable scenarios. Each object is stored as a separate XML file using the naming convention ClassName.ObjectId.UpdateCount.xml, organized into folders by storage name.

Key advantages:

  • No database server required
  • Human-readable format
  • Simple backup (copy folder)
  • Zero configuration

Key limitations:

  • Poor performance for large datasets (all files loaded into memory)
  • No real transactions
  • Limited concurrency support
  • No server-side filtering

Best for: Prototypes, small applications (< 10,000 objects), portable apps, offline scenarios. Not recommended for: Large datasets, multi-user applications, high-concurrency scenarios.

Released under Mozilla License, Version 2.0.