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.
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:
| Aspect | SAX / Event-based (Expat) | DOM / Tree-based |
|---|---|---|
| Memory usage | Constant — only current node | Proportional to document size |
| Large files | Handles multi-GB files easily | Runs out of memory |
| Access pattern | Forward-only, one pass | Random access to any node |
| Speed | Very fast (single pass, no allocation) | Slower (build + traverse tree) |
| Use case | Logs, exports, data pipelines | Config 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:
| Event | Enum value | When fired | Key fields |
|---|---|---|---|
| StartElement | eeStartElement | <tag attr="val"> opens | Element.Name, Element.Attributes |
| EndElement | eeEndElement | </tag> closes | ElementName |
| CharData | eeCharData | Text content between tags | Text |
| Comment | eeComment | <!-- comment --> | Text |
| PI | eePI | <?target data?> | PITarget, Text |
| StartCData | eeStartCData | <![CDATA[ opens | — |
| EndCData | eeEndCData | ]]> closes CDATA section | — |
| StartNamespace | eeStartNS | xmlns: declaration seen | NSPrefix, NSURI |
| EndNamespace | eeEndNS | Namespace scope ends | NSPrefix |
| EOF | eeEOF | Document fully parsed | — |
| Error | eeError | Malformed XML encountered | ErrorMsg, Line, Column |
Used By
Expat is one of the most widely deployed XML parsers in the world:
| Project | Usage |
|---|---|
| Python | xml.parsers.expat — the standard library XML event parser |
| Apache httpd | Parses configuration and WebDAV XML bodies |
| Subversion | Parses WebDAV / DeltaV protocol responses |
| Firefox | Used in the XML parsing pipeline for non-HTML content |
Comparison
| Library | Style | Strengths | When to prefer |
|---|---|---|---|
| expat | SAX / event streaming | Fastest, lowest memory, C-proven | Large files, pipelines, log processing |
| libxml2 | DOM + XPath + XSLT | Full-featured, XPath queries, XSLT transforms | Small docs needing querying or transformation |
| xml-parser (PAI) | DOM tree in PascalAI | Idiomatic PAI, navigate tree with dot notation | Config 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.
| Field | Type | Description |
|---|---|---|
| Name | string | Attribute name (may include namespace prefix) |
| Value | string | Attribute value after entity expansion |
TExpatElement
Populated for eeStartElement events.
| Field | Type | Description |
|---|---|---|
| Name | string | Full element name (or URI|LocalName in NS mode) |
| Attributes | array of TExpatAttr | All attributes declared on this element |
| Namespace | string | Namespace URI; empty if not namespace-aware |
| LocalName | string | Local 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.
| Field | Type | Populated when |
|---|---|---|
| Kind | TExpatEvent | Always |
| Element | TExpatElement | eeStartElement |
| ElementName | string | eeEndElement |
| Text | string | eeCharData, eeComment, eePI |
| PITarget | string | eePI — the processing instruction target |
| NSPrefix | string | eeStartNS, eeEndNS |
| NSURI | string | eeStartNS |
| Line | Integer | All events (source position) |
| Column | Integer | All events (source position) |
| ErrorMsg | string | eeError |
TExpatOptions
Configuration passed when creating a parser via NewParser/NewNSParser.
| Field | Type | Description |
|---|---|---|
| Encoding | string | 'UTF-8', 'UTF-16', 'ISO-8859-1', or '' to auto-detect from XML declaration |
| Namespaces | Boolean | Enable namespace processing (use NewNSParser instead) |
| NamespaceSep | Char | Separator between namespace URI and local name; '|' is conventional |
| SkipWhiteSpace | Boolean | Suppress 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.
| Function | Description |
|---|---|
| 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
| Function | Returns | Description |
|---|---|---|
| ParseString(P, XML) | Boolean | Parse a complete XML string in one call. Returns True on success. |
| ParseChunk(P, Data, IsFinal) | Boolean | Parse an incremental chunk. Set IsFinal=True on the last chunk to signal end of document. |
| ParseFile(P, Path) | Boolean | Open and stream-parse a file. Efficient for large files; reads in chunks internally. |
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
| Function | Returns | Description |
|---|---|---|
| NextEvent(P) | TExpatEventData | Pull the next queued event from the parser. Call in a loop until Kind = eeEOF or Kind = eeError. |
| ParseAllEvents(XML) | array of TExpatEventData | Convenience: 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.
| Function | Returns | Description |
|---|---|---|
| GetError(P) | string | Human-readable description of the last parse error. |
| GetErrorLine(P) | Integer | Line number (1-based) where the error occurred. |
| GetErrorColumn(P) | Integer | Column 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.
| Function | Returns | Description |
|---|---|---|
| ExtractText(XML) | string | Concatenate all CharData content, stripping every tag. Useful for getting readable text from XHTML or docbook. |
| ExtractElements(XML, ElementName) | array of string | Return the text content of every occurrence of the named element. |
| ExtractAttr(XML, ElementName, AttrName) | array of string | Return 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
| Function | Returns | Description |
|---|---|---|
| ExpatVersion | string | Returns the version string of the underlying libexpat (e.g. "expat_2.5.0"). |
Package Info
System Requirements
libexpat1-devUbuntu/Debian:
sudo apt install libexpat1-dev
TExpatEvent Values
eeStartElementeeEndElementeeCharDataeeCommenteePIeeStartCDataeeEndCDataeeStartNSeeEndNSeeEOFeeError
Functions
- NewParser
- NewNSParser
- FreeParser
- ResetParser
- ParseString
- ParseChunk
- ParseFile
- NextEvent
- ParseAllEvents
- GetError
- GetErrorLine
- GetErrorColumn
- ExtractText
- ExtractElements
- ExtractAttr
- ExpatVersion
See Also
- libxml2 (DOM + XPath + XSLT)
- xml-parser (PAI DOM parser)
- libxslt (XSLT transforms)
- msgpack (binary serialization)