expat clib XML / Parsing

Event-based XML parser. SAX-style: fires callbacks for elements, text, comments as it streams. Fast, low memory, used by Python and Apache.

ppm install expat

Overview

expat wraps libexpat — the battle-tested stream-oriented XML parser written in C. It is the XML engine inside Python's xml.parsers.expat, Apache httpd, Subversion, and Firefox's XML stack.

Instead of building an in-memory tree, expat fires callbacks (events) as it reads each part of the document. This makes parsing genuinely streaming: you see elements as they arrive, memory stays flat, and multi-gigabyte XML files are no harder than small ones.

CLib package

Requires libexpat1-dev. Ubuntu/Debian: sudo apt install libexpat1-dev

SAX Parsing Model

Expat is a SAX-style (Simple API for XML) parser. The distinction from DOM-based parsers is fundamental:

AspectSAX / Event-based (Expat)DOM / Tree-based
Memory usageConstant — only current nodeProportional to document size
Large filesHandles multi-GB files easilyRuns out of memory
Access patternForward-only, one passRandom access to any node
SpeedVery fast (single pass, no allocation)Slower (build + traverse tree)
Use caseLogs, exports, data pipelinesConfig files, small documents needing queries

With expat in PascalAI, you call ParseString or ParseFile and then poll events with NextEvent, or use ParseAllEvents for small documents that fit in memory.

Event Types

Every call to NextEvent returns a TExpatEventData whose Kind field is one of:

EventEnum valueWhen firedKey fields
StartElementeeStartElement<tag attr="val"> opensElement.Name, Element.Attributes
EndElementeeEndElement</tag> closesElementName
CharDataeeCharDataText content between tagsText
CommenteeComment<!-- comment -->Text
PIeePI<?target data?>PITarget, Text
StartCDataeeStartCData<![CDATA[ opens
EndCDataeeEndCData]]> closes CDATA section
StartNamespaceeeStartNSxmlns: declaration seenNSPrefix, NSURI
EndNamespaceeeEndNSNamespace scope endsNSPrefix
EOFeeEOFDocument fully parsed
ErroreeErrorMalformed XML encounteredErrorMsg, Line, Column

Used By

Expat is one of the most widely deployed XML parsers in the world:

ProjectUsage
Pythonxml.parsers.expat — the standard library XML event parser
Apache httpdParses configuration and WebDAV XML bodies
SubversionParses WebDAV / DeltaV protocol responses
FirefoxUsed in the XML parsing pipeline for non-HTML content

Comparison

LibraryStyleStrengthsWhen to prefer
expatSAX / event streamingFastest, lowest memory, C-provenLarge files, pipelines, log processing
libxml2DOM + XPath + XSLTFull-featured, XPath queries, XSLT transformsSmall docs needing querying or transformation
xml-parser (PAI)DOM tree in PascalAIIdiomatic PAI, navigate tree with dot notationConfig files, small structured documents

Quick Start

Parse an XML string and collect events

uses expat;

var xml := '<root><item id="1">Hello</item><item id="2">World</item></root>';

var events := ParseAllEvents(xml);

for var i := 0 to Length(events) - 1 do
begin
  var ev := events[i];
  case ev.Kind of
    eeStartElement: WriteLn('<' + ev.Element.Name + '> attrs=' +
                             IntToStr(Length(ev.Element.Attributes)));
    eeEndElement:   WriteLn('</' + ev.ElementName + '>');
    eeCharData:     WriteLn('text: ' + ev.Text);
    eeError:        WriteLn('ERROR at line ' + IntToStr(ev.Line) +
                             ': ' + ev.ErrorMsg);
  end;
end;

Extract all text content from XML

uses expat;

var html := ReadFile('page.xml');
var plainText := ExtractText(html);
WriteLn(plainText);

{ Extract all href values from <a> elements }
var hrefs := ExtractAttr(html, 'a', 'href');
for var i := 0 to Length(hrefs) - 1 do
  WriteLn(hrefs[i]);

Namespace-aware parsing

uses expat;

var xml :=
  '<root xmlns:dc="http://purl.org/dc/elements/1.1/">' +
  '  <dc:title>My Document</dc:title>' +
  '</root>';

{ Use NewNSParser — element names include URI separated by '|' }
var p := NewNSParser('', '|'[1]);

if ParseString(p, xml) then
begin
  var ev := NextEvent(p);
  while ev.Kind <> eeEOF do
  begin
    if ev.Kind = eeStartNS then
      WriteLn('NS: ' + ev.NSPrefix + ' = ' + ev.NSURI);
    if ev.Kind = eeStartElement then
      WriteLn('Element: ' + ev.Element.Name);
    ev := NextEvent(p);
  end;
end
else
  WriteLn('Parse error: ' + GetError(p) +
          ' at line ' + IntToStr(GetErrorLine(p)));

FreeParser(p);

Types

TExpatParser

An opaque integer handle returned by NewParser or NewNSParser. Pass it to all subsequent calls. Free with FreeParser when done, or call ResetParser to reuse it for another document without reallocating.

TExpatAttr

A single XML attribute on a start element.

FieldTypeDescription
NamestringAttribute name (may include namespace prefix)
ValuestringAttribute value after entity expansion

TExpatElement

Populated for eeStartElement events.

FieldTypeDescription
NamestringFull element name (or URI|LocalName in NS mode)
Attributesarray of TExpatAttrAll attributes declared on this element
NamespacestringNamespace URI; empty if not namespace-aware
LocalNamestringLocal part of the name when namespace-aware

TExpatEvent

Enumeration of all event kinds. Values: eeStartElement, eeEndElement, eeCharData, eeComment, eePI, eeStartCData, eeEndCData, eeStartNS, eeEndNS, eeEOF, eeError.

TExpatEventData

The complete data payload for one event.

FieldTypePopulated when
KindTExpatEventAlways
ElementTExpatElementeeStartElement
ElementNamestringeeEndElement
TextstringeeCharData, eeComment, eePI
PITargetstringeePI — the processing instruction target
NSPrefixstringeeStartNS, eeEndNS
NSURIstringeeStartNS
LineIntegerAll events (source position)
ColumnIntegerAll events (source position)
ErrorMsgstringeeError

TExpatOptions

Configuration passed when creating a parser via NewParser/NewNSParser.

FieldTypeDescription
Encodingstring'UTF-8', 'UTF-16', 'ISO-8859-1', or '' to auto-detect from XML declaration
NamespacesBooleanEnable namespace processing (use NewNSParser instead)
NamespaceSepCharSeparator between namespace URI and local name; '|' is conventional
SkipWhiteSpaceBooleanSuppress eeCharData events that contain only whitespace

Parser Lifecycle API

Create one parser per document. Use ResetParser to reuse the same handle for multiple sequential documents without re-allocating.

FunctionDescription
NewParser(Encoding)Create a new parser. Pass '' to auto-detect encoding from the XML declaration.
NewNSParser(Encoding, Sep)Create a namespace-aware parser. Element names will be URI<Sep>LocalName.
FreeParser(P)Release all resources held by the parser.
ResetParser(P, Encoding)Reset state to parse a new document. Faster than free + new.

Parsing API

FunctionReturnsDescription
ParseString(P, XML)BooleanParse a complete XML string in one call. Returns True on success.
ParseChunk(P, Data, IsFinal)BooleanParse an incremental chunk. Set IsFinal=True on the last chunk to signal end of document.
ParseFile(P, Path)BooleanOpen and stream-parse a file. Efficient for large files; reads in chunks internally.
Tip — streaming large files

Use ParseFile for files over a few MB. It reads in buffered chunks so the full file is never loaded into memory at once. Process events between chunks with NextEvent.

Event Polling API

FunctionReturnsDescription
NextEvent(P)TExpatEventDataPull the next queued event from the parser. Call in a loop until Kind = eeEOF or Kind = eeError.
ParseAllEvents(XML)array of TExpatEventDataConvenience: parse an XML string and return all events as an array. Not suitable for large documents.

Error Handling API

When ParseString, ParseChunk, or ParseFile returns False, use these functions to get details. Errors are also surfaced as eeError events when using NextEvent.

FunctionReturnsDescription
GetError(P)stringHuman-readable description of the last parse error.
GetErrorLine(P)IntegerLine number (1-based) where the error occurred.
GetErrorColumn(P)IntegerColumn number (1-based) where the error occurred.
uses expat;

var p := NewParser('');
if not ParseString(p, '<root><unclosed>') then
begin
  WriteLn('Error: ' + GetError(p));
  WriteLn('  at line ' + IntToStr(GetErrorLine(p)) +
          ', col '  + IntToStr(GetErrorColumn(p)));
end;
FreeParser(p);

Streaming Convenience API

High-level helpers for common extraction tasks that avoid manual event loops.

FunctionReturnsDescription
ExtractText(XML)stringConcatenate all CharData content, stripping every tag. Useful for getting readable text from XHTML or docbook.
ExtractElements(XML, ElementName)array of stringReturn the text content of every occurrence of the named element.
ExtractAttr(XML, ElementName, AttrName)array of stringReturn the value of the named attribute from every occurrence of the named element.
uses expat;

var feed := ReadFile('rss.xml');

{ Get all article titles }
var titles := ExtractElements(feed, 'title');
for var i := 0 to Length(titles) - 1 do
  WriteLn(titles[i]);

{ Get all link href values }
var links := ExtractAttr(feed, 'link', 'href');
WriteLn(IntToStr(Length(links)) + ' links found');

Info

FunctionReturnsDescription
ExpatVersionstringReturns the version string of the underlying libexpat (e.g. "expat_2.5.0").

Package Info

Version1.0.0
Typeclib
C Librarylibexpat1-dev
Authorgustavo

System Requirements

libexpat1-dev

Ubuntu/Debian:
sudo apt install libexpat1-dev

TExpatEvent Values

eeStartElement
eeEndElement
eeCharData
eeComment
eePI
eeStartCData
eeEndCData
eeStartNS
eeEndNS
eeEOF
eeError

Functions

  • NewParser
  • NewNSParser
  • FreeParser
  • ResetParser
  • ParseString
  • ParseChunk
  • ParseFile
  • NextEvent
  • ParseAllEvents
  • GetError
  • GetErrorLine
  • GetErrorColumn
  • ExtractText
  • ExtractElements
  • ExtractAttr
  • ExpatVersion

See Also