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.