NAME
Text::KDL::XS - KDL Document Language parser and emitter built on libckdl
SYNOPSIS
use utf8; # this source file is UTF-8
use Text::KDL::XS qw(parse_kdl emit_kdl);
binmode STDOUT, ':encoding(UTF-8)'; # parsed strings are Perl characters
# Parse a character string (or a filehandle, or a code reference returning chunks).
my $doc = parse_kdl(<<'KDL');
package "kdl-rs" {
version "0.4.0"
author "Kat Marchán" email="kat@example.com"
keywords "config" "data"
}
KDL
# Walk the tree.
for my $node (@{ $doc->nodes }) {
print $node->name, "\n"; # package
print $node->args->[0]->as_string, "\n"; # kdl-rs
for my $child (@{ $node->children }) {
printf " %s", $child->name;
printf " %s", $_->as_string for @{ $child->args };
printf " %s=%s", $_->[0], $_->[1]->as_string for @{ $child->props };
print "\n";
}
}
# Look up a property, with type information intact.
my $email = $doc->nodes->[0]->children->[1]->prop('email');
print $email->type; # string
print $email->as_string; # kat@example.com
# Write it back out (tree mode: preserves order, kinds and annotations).
print emit_kdl($doc);
# Or serialise plain Perl data (data mode).
print emit_kdl({ server => { host => 'localhost', port => 8080 } });
# server {
# host localhost
# port 8080
# }
DESCRIPTION
Text::KDL::XS reads and writes documents in the KDL Document Language, a small configuration and data language that looks like this:
node "argument" key="value" {
child 1 2 3
(type)annotated #true
}
It is a thin XS binding to ckdl, a C11 implementation that passes the official KDL test suites. Both KDL 2.0.0 (current) and KDL 1.0.0 (legacy) are supported, and the version is detected automatically unless you pin it.
The distribution provides:
"parse_kdl", which turns KDL text into a tree of Text::KDL::XS::Document, Text::KDL::XS::Node and Text::KDL::XS::Value objects.
"emit_kdl", which turns such a tree, or plain Perl hashes and arrays, back into KDL text.
Text::KDL::XS::Parser, a streaming (SAX-style) event parser for documents that should not be held in memory at once.
A tour of every KDL feature with runnable examples is in Text::KDL::XS::Cookbook. This page is the API reference.
QUICK REFERENCE
- parse a string, a file, a stream
- choose KDL v1 or v2, detect the version
- write KDL from Document and Node objects
- write KDL from hashes and arrays
- control indentation, escaping, quoting
- read comments and slashdashed elements
- access nodes, arguments, properties, children
- strings, numbers, booleans, null, type annotations
- big integers, 1e400, #inf, #nan, number kinds
-
"VALUE MODEL" in Text::KDL::XS::Value, "ARBITRARY PRECISION NUMBERS" in Text::KDL::XS::Value
- UTF-8 and character strings
- filehandles, STDIN, pipes, in-memory handles
- untrusted input, nesting limits
-
"parse_kdl options", "Parse untrusted input" in Text::KDL::XS::Cookbook
- what dies and when
- threads and fork
- known problems and workarounds
- feature-by-feature examples and recipes
EXPORTS
Nothing is exported by default. Request the functions you need:
use Text::KDL::XS qw(parse_kdl emit_kdl);
Loading Text::KDL::XS also loads Text::KDL::XS::Parser, Text::KDL::XS::Document, Text::KDL::XS::Node, Text::KDL::XS::Value and Text::KDL::XS::Emitter. Each of these modules can also be loaded on its own; it loads Text::KDL::XS itself.
FUNCTIONS
parse_kdl
my $doc = parse_kdl($source);
my $doc = parse_kdl($source, version => '2');
my $doc = parse_kdl($source, %options);
Parses a complete KDL document and returns a Text::KDL::XS::Document. Dies on malformed input (see "ERRORS").
Sources
$source is one of:
- A string
-
The document text as a Perl character string: a string literal in a source file with
use utf8, text read through an:encoding(UTF-8)layer, the result ofEncode::decode, or the output of "emit_kdl". This is the fastest source; the string is copied once and ckdl reads from the copy.A string of UTF-8 bytes (read through a
:rawhandle, or a literal withoutuse utf8) must be decoded first, withutf8::decodeorEncode::decode('UTF-8', ...), or passed as a filehandle instead; otherwise every byte is taken as one character. See "ENCODING". - A filehandle or IO object
-
A glob (
*STDIN), a glob reference (\*STDIN,$fhfromopen), an IO object (*STDIN{IO}, IO::Handle, IO::File, IO::Socket), a tied handle, or any other object with areadorsysreadmethod. Open handles are read with Perl'sread, so every PerlIO layer, data buffered by earlier reads and in-memory handles work. See "FILEHANDLE SOURCES". - A code reference
-
Called as
$code->($wanted_bytes)whenever the parser needs more input. It returns the next chunk of the document, or an empty string orundefat the end of the input; after that it is not called again.Chunks are UTF-8 bytes. A character string (a string with Perl's UTF-8 flag on) is used in its UTF-8 encoding, so returning characters works as well.
$wanted_bytesis only a hint: a longer chunk is kept and handed to ckdl in pieces, and chunk boundaries need not align with lines, tokens or even characters.The first call happens while the parser is created, before
parse_kdlor "new" in Text::KDL::XS::Parser returns: ckdl reads ahead to look for a byte order mark. An exception thrown by the code reference propagates unchanged (an exception object stays the same object) out of the call that needed the input; a reference returned as a chunk dies.
parse_kdl options
- version => 'detect' | '1' | '2'
-
Which KDL syntax to accept. The default
'detect'accepts both and settles on one at the first version-specific construct (#trueversustrue,#"raw"#versusr"raw", a bare identifier used as a value).'1'and'2'accept exactly one version and reject the other's syntax.'v1'and'v2'are accepted as well, in any letter case. Any other value dies withText::KDL::XS::Parser: unknown version '...' (expected 'detect', '1' or '2').See "KDL VERSIONS" for how the versions differ.
- max_depth => $levels
-
The deepest nesting of nodes allowed; a top-level node is at depth 1. Default 512;
0means unlimited. A document nested deeper dies withKDL parse error: nesting depth exceeds max_depth (512)as soon as the parser reaches the extra level, before the tree gets any deeper. This bounds the recursion of "emit_kdl", "as_data" in Text::KDL::XS::Node and your own tree walks for documents from untrusted sources. - emit_comments => 0 | 1
-
Accepted for symmetry with Text::KDL::XS::Parser, where it makes the parser report comments and slashdashed elements. It does not change the result of
parse_kdl: comments and elements commented out with/-never become part of the tree.
Unknown options, an odd number of option arguments and invalid values die. An option whose value is undef is treated as not given.
Return value
A Text::KDL::XS::Document. Its nodes method returns the top-level nodes as Text::KDL::XS::Node objects; every argument and property value is a Text::KDL::XS::Value. Node names, property keys, string values and type annotations are returned as Perl character strings. An empty or comment-only document gives a document with no nodes.
emit_kdl
my $text = emit_kdl($document);
my $text = emit_kdl($node);
my $text = emit_kdl(\@nodes);
my $text = emit_kdl(\%data);
my $text = emit_kdl(\@data);
my $text = emit_kdl($anything, %options);
Serialises a tree or a plain data structure to KDL and returns the text as a Perl character string (encode it as UTF-8 before writing it to a file; see "ENCODING"). The output ends with a newline; an empty document is a single newline. Everything is validated before it is written, so emit_kdl either returns a document that parses back to the same data or dies.
The mode is chosen from the type of the first argument.
Tree mode
Selected when the argument is a Text::KDL::XS::Document, a Text::KDL::XS::Node, or a non-empty array reference whose elements are all Text::KDL::XS::Node objects; subclasses of these classes are accepted everywhere. An array that mixes nodes with anything else is data mode, where the nodes make it die.
Tree mode is faithful: it writes arguments and properties in their stored order, keeps type annotations on nodes and values, keeps the integer/float/string distinction of numbers (1.0 stays 1.0), and writes repeated properties as often as they occur. It is the mode to use for round-tripping a parsed document. What it does not preserve (comments, layout, number spelling) is listed in "What a round trip loses" in Text::KDL::XS::Cookbook.
Argument and property values inside the tree are normally Text::KDL::XS::Value objects, but plain scalars and objects are accepted too and are converted as described under "Scalar coercion". A node that is its own descendant dies with emit_kdl: cyclic data structure; a node that appears in several places is written in each.
Data mode
Selected for any other unblessed hash or array reference. Data mode is a convenience for writing configuration from ordinary Perl data; it is deterministic but lossy. Every hash key becomes a node; data mode never writes properties.
Perl value Emitted as
------------------------------- ----------------------------------------------
{ key => $scalar } key <value>
{ key => undef } key #null
{ key => [ $s1, $s2, ... ] } key <s1> <s2> ... (all elements scalars)
{ key => [] } key (bare node)
{ key => {} } key (bare node)
{ key => { ... } } key { <children> }
{ key => [ {...}, {...} ] } key { ... } key { ... } (one sibling per element)
{ key => [ $s, {...} ] } key <s> key { ... } (mixed: one sibling per element)
{ key => [ [1,2], [3] ] } key 1 2 key 3 (inner arrays: one sibling each)
[ $a, $b, ... ] (top level) - <a> - <b> ... (nodes named "-")
{} or [] (top level) (a single newline)
boolean object #true / #false
Text::KDL::XS::Value object as the object says (type, kind, annotation)
Hash keys are emitted in sorted order. Single values are classified by "Scalar coercion". A hash or array that contains itself dies with emit_kdl: cyclic data structure; one that is referenced from several places is written in each.
What data mode cannot express: properties; arguments and children on the same node; a specific node order (keys are sorted); type annotations on nodes; and the difference between { key => 'a' } and { key => ['a'] } (both give key a). Build a Text::KDL::XS::Node tree when you need any of these.
Scalar coercion
Single values that are not Text::KDL::XS::Value objects, in either mode, are mapped like this:
Perl value KDL value
---------------------------------------------- ----------------------------
undef #null
JSON::PP::Boolean, Types::Serialiser::Boolean,
JSON::Boolean, boolean, Mojo::JSON::_Bool
(or a subclass) #true / #false (by truthiness)
Text::KDL::XS::Value (or a subclass) as specified by the object
a number: a scalar with an integer or floating
point value and no string value, or one whose
string value is exactly Perl's rendering of
that number number (integer or float)
any other plain scalar string
Math::BigInt, Math::BigFloat number, written with its exact digits
another object with string overloading string ("$object")
any other object or reference dies
A number with an integer value is written as an integer over the whole native range (-2**63 to 2**64-1, unsigned values included); any other number as a float, with the shortest text that reads back as the same double (0.30000000000000004, 1e+21, -0.0, 123456789.0; a float always has a decimal point or an exponent). A scalar that has both an integer and a floating point value, such as 3.0 after it has been compared with ==, is written as an integer.
The decision uses the scalar's value, not how it looks. '42' from a string literal is a string and 42 is a number. A string that has been used as a number is a number only when its text is exactly the number Perl would print: '42' after $x + 0 becomes the number 42, but '007', '1.50', '1e3', ' 42 ' and dualvars stay strings, so no text is ever changed. Perl's false value !!0 is the empty string, as in JSON::PP. Force one or the other with "$x" or 0 + $x. The strings 'true' and 'false' are never promoted to booleans.
Math::BigInt and Math::BigFloat objects become numbers with their exact digits (NaN and infinity die with emit_kdl: cannot write the Math::BigInt NaN as a KDL number). Any other object with a string conversion (URI, Path::Tiny, ...) is written as a string. Objects without one, and references other than the hashes and arrays of data mode (code, scalar and glob references), die with emit_kdl: cannot serialize Foo object or emit_kdl: cannot serialize CODE ref.
emit_kdl options
- version => 'detect' | '1' | '2'
-
Output syntax.
'2'(and'detect', the default) writes KDL 2.0.0:#true,#null, bare identifier strings where possible.'1'writes KDL 1.0.0:true,null, every string value quoted.'v1'and'v2'are accepted as well, in any letter case. KDL v1 has no spelling for infinity and NaN, so a non-finite float dies in v1 output withemit_kdl: KDL v1 has no representation for inf/nan; v2 writes#inf,#-infand#nan. - indent => $columns
-
Number of spaces per nesting level, an integer from 0 to 64. Default 4.
- escape_mode => $bitmask
-
Which characters inside quoted strings are written as escape sequences. Values are combinations of the ckdl
kdl_escape_modeflags:0 minimal: " and \, plus (in v2 output) the characters KDL never allows literally: U+0000 to U+0008, U+000E to U+001F, U+007F, the bidi controls and U+FEFF, which are always written as \u{...} 0x10 also escape backspace (\b) and vertical tab 0x20 also escape newline characters: LF, CR, FF, NEL, LS, PS 0x40 also escape tabs 0x70 default (control characters, newlines and tabs) 0x170 ASCII only: every non-ASCII character becomes \u{...}0x10,0x20and0x40combine with bitwise or;0x170is a preset (0x100has an effect only together with all of0x70). Any other bit dies. KDL v2 does not allow a newline inside a quoted string, so for v2 output ('2'and'detect')0x20is always added. In v1 output0really is minimal and writes newlines and control characters literally. - identifier_mode => 0 | 1 | 2
-
How node names, property keys, type annotations and (in v2) string values are written:
0 bare whenever the characters allow it 1 always quoted 2 bare only when pure ASCIIWithout this option,
emit_kdlwrites identifiers bare where possible (mode 0) and switches the whole document to mode 1 when a name, key, annotation or v2 string value would otherwise be read back as something else: a keyword (true,false,null; in v2 alsoinf,-inf,nan) or a number (-1,+1,.5). So the output always parses back to the same data. When you passidentifier_mode, it is used as given: with mode 0 or 2 such strings are written bare and do not round-trip.
Unknown options, an odd number of option arguments and invalid values die. An option whose value is undef is treated as not given.
ENCODING
KDL documents are UTF-8 by definition. The rules for this module are:
A string passed to "parse_kdl" or "new" in Text::KDL::XS::Parser is a Perl character string.
Filehandles and code references deliver the document as UTF-8 bytes. A handle with an
:encoding(UTF-8)or:utf8layer and a code reference returning character strings work too: characters are encoded to UTF-8 before they reach the parser.Everything the parser returns (names, keys, strings, annotations, comment text) is a Perl character string.
"emit_kdl" takes Perl character strings and returns a character string. Encode it when writing it out, either explicitly (
encode('UTF-8', $text)) or through an:encoding(UTF-8)output layer. Printing it to a handle without a layer writes a string whose non-ASCII characters are all below U+0100 as Latin-1 bytes (wrong for a UTF-8 consumer), and a string containing a character above U+00FF as UTF-8 with a "Wide character" warning.
So parse_kdl(emit_kdl($data)) always works, and so does parsing text read through an :encoding(UTF-8) layer. A string of UTF-8 bytes, such as a heredoc in a source file without use utf8 or data slurped through a :raw handle, must be decoded first:
my $doc = parse_kdl(Encode::decode('UTF-8', $bytes));
utf8::decode($bytes) or die "not UTF-8"; # in place, no module needed
my $doc = parse_kdl($bytes);
Text::KDL::XS 0.001 read string sources as UTF-8 bytes; code that relied on that has to decode as shown above (or pass the filehandle).
Only Unicode text is accepted, in both directions. Input that is not well-formed UTF-8, or that encodes a surrogate (U+D800 to U+DFFF) or a code point above U+10FFFF, dies with KDL parse error: input is not valid UTF-8; a \u{...} escape that produces such a code point dies with KDL parse error: string contains a surrogate or a code point above U+10FFFF. emit_kdl dies when a name, key, annotation or string value contains one (emit_kdl: string value contains a surrogate or a code point above U+10FFFF) instead of writing something else.
FILEHANDLE SOURCES
An open filehandle is read with Perl's read, in chunks of the size the underlying library asks for (a few kilobytes). Consequences:
Any PerlIO layer works. A
:rawhandle delivers UTF-8 bytes, an:encoding(UTF-8)or:utf8handle delivers characters, which are encoded again;:crlfis harmless because KDL accepts CRLF line ends.Reading continues where the handle stands: lines read with
<$fh>before are not seen by the parser, and nothing that PerlIO has buffered is lost.In-memory handles (
open my $fh, '<', \$string), tied handles and bare globs such as*STDINwork.readwaits until it has the requested number of bytes or the input ends. On a pipe or socket the first events can therefore arrive later than the data they describe. When latency matters, pass a code reference that usessysread(see "Read from STDIN, a socket or a pipe" in Text::KDL::XS::Cookbook).A read error dies with
Text::KDL::XS::Parser: read failed: ...(the text of$!); a closed filehandle dies withText::KDL::XS::Parser: filehandle is not open.
An object that is not a filehandle but has a read method (or, failing that, sysread) is read through it. The method is called like Perl's read, as $object->read($buffer, $length), and must return the number of bytes or characters read, 0 at the end of the input, or undef on error.
KDL VERSIONS
KDL 1.0.0 (2021) and KDL 2.0.0 (2024) share most of their syntax, and any document that parses under both versions has the same meaning under both. The differences that matter most when reading or writing with this module are the # prefix on #true, #false and #null; bare identifier strings as values (v2 only); raw strings #"..."# (v2) versus r"..." (v1); multi-line strings """ (v2) versus literal newlines inside quotes (v1); and the keyword numbers #inf, #-inf and #nan (v2 only). The complete table is in "Differences between KDL v1 and v2" in Text::KDL::XS::Cookbook.
With the default version => 'detect', the parser accepts either until the first construct that only exists in one version, and then requires that version for the rest of the document. To reject the other version outright, pin version. The parser does not report which version it detected; "Version detection" in Text::KDL::XS::Cookbook shows how to find out.
emit_kdl defaults to v2 output. Pass version => '1' to write legacy documents; the parsed data is identical either way, so converting a document between versions is a parse followed by an emit ("Converting between versions" in Text::KDL::XS::Cookbook). A document containing #inf, #-inf or #nan cannot be converted to v1.
ERRORS
All errors are exceptions (die). Messages end with the location of the call in your code (at script.pl line 12.), not a line inside this distribution. The exception is an exception thrown by a source code reference, which is passed through exactly as thrown.
Parsing
KDL parse error: REASON-
The input is not valid KDL for the selected version.
REASONis the explanation of the underlying library, for exampleUnexpected end of data (unclosed lists of children),Dangling slashdash (/-),Whitespace required before argument or propertyorBare identifier not allowed here. There is no line or column: the library does not track positions. Raised byparse_kdland by "next_event" in Text::KDL::XS::Parser. After an error the parser is finished: every furthernext_eventdies with the same error. KDL parse error: input is not valid UTF-8KDL parse error: string contains a surrogate or a code point above U+10FFFF-
See "ENCODING". The second message names the field:
node name,property key,type annotation,stringorcomment. KDL parse error: nesting depth exceeds max_depth (512)-
The document is nested deeper than the
max_depthoption allows. Text::KDL::XS::Parser: source is required-
parse_kdl(undef). Text::KDL::XS::Parser: unsupported source ref type 'X'-
The source was a reference that is neither a code reference nor a filehandle nor an object with a
readorsysreadmethod (for example an array or hash reference). Text::KDL::XS::Parser: filehandle is not openText::KDL::XS::Parser: read failed: ...-
See "FILEHANDLE SOURCES".
Text::KDL::XS::Parser: the source callback must return a string or undef, not a reference-
A code reference source returned a reference.
Text::KDL::XS::Parser: next_event called from inside the parser's own source callback-
A source code reference called
next_eventon the parser it feeds. Text::KDL::XS::Parser: unknown version 'X' (expected 'detect', '1' or '2')Text::KDL::XS::Parser: unknown option 'X'Text::KDL::XS::Parser: expected name => value pairs, got an odd number of argumentsText::KDL::XS::Parser: max_depth must be a non-negative integer, got 'X'-
Bad options to
parse_kdlor "new" in Text::KDL::XS::Parser.
Emitting
emit_kdl: expected Document, Node, ARRAY ref, or HASH ref-
emit_kdlwas given a plain scalar, a code reference or an object that is not a document or node. emit_kdl: cannot serialize X object,emit_kdl: cannot serialize X ref-
A value that "Scalar coercion" does not accept: an object without a string conversion, or a reference other than the hashes and arrays of data mode.
emit_kdl: cyclic data structure-
A hash, array or node contains itself.
emit_kdl: tree mode expects Text::KDL::XS::Node, got X-
An element of
$node->childrenor$doc->nodesis not a node. emit_kdl: a property must be a [ key => value ] pair-
An element of
$node->propsis not an array reference. emit_kdl: node name must be defined,emit_kdl: property key must be defined-
A hand-built node without a name, or a property with an undefined key.
emit_kdl: node name contains a surrogate or a code point above U+10FFFF-
See "ENCODING"; the message names the field.
emit_kdl: KDL v1 has no representation for inf/nan-
A non-finite float in
version => '1'output. emit_kdl: cannot write the Math::BigInt NaN as a KDL number-
A Math::BigInt or Math::BigFloat that is NaN or infinite.
emit_kdl: unknown version 'X' (expected 'detect', '1' or '2')emit_kdl: indent must be an integer from 0 to 64, got 'X'emit_kdl: escape_mode must be a combination of 0x10, 0x20, 0x40 and 0x170, got 'X'emit_kdl: identifier_mode must be an integer from 0 to 2, got 'X'emit_kdl: unknown option 'X'-
Bad options to
emit_kdl. emit_kdl: 'X' is not a KDL number,emit_kdl: unknown number kind 'X' ...,emit_kdl: unknown value type 'X' ...-
A Text::KDL::XS::Value whose hash was changed by hand to something that "new" in Text::KDL::XS::Value would have refused. The emitter checks every value again, so that nothing but valid KDL is ever written.
Constructors
"new" in Text::KDL::XS::Value, "new" in Text::KDL::XS::Node and "new" in Text::KDL::XS::Document die with messages starting with their class name, for example Text::KDL::XS::Value->new: unknown type 'Number' (expected null, bool, number or string); see the class documentation. Calling a method of Text::KDL::XS::Parser on something that is not a parser object made by its constructor dies with not a valid Text::KDL::XS::Parser object.
Warnings
"as_number" in Text::KDL::XS::Value on a string value that is not numeric warns Argument "..." isn't numeric, like any numeric use of such a string.
KNOWN ISSUES AND LIMITATIONS
The remaining limitations come from the underlying ckdl library or from KDL itself.
- Parse errors carry no position
-
ckdl does not track lines or columns, so errors have a reason but no location in the document.
- Keyword-like strings switch the whole document to quoted identifiers
-
ckdl would write a string such as
trueor-1without quotes, which reads back as a keyword or a number.emit_kdldetects this and writes the document with every identifier quoted (see "emit_kdl options"), which is correct but more verbose than needed. - A children block needs whitespace before it
-
KDL 2.0.0 requires it, so
node{}is correctly rejected in v2, but ckdl rejects it in v1 mode as well although KDL 1.0.0 allows it. Writenode {}. - Detect mode is not a complete KDL v1 parser
-
ckdl documents the hybrid mode as exact for v2 and for almost all v1 documents. Pin
version => '1'for strict v1. - Detect mode accepts
.5as an identifier -
.5,-.5and+.5are rejected by bothversion => '1'andversion => '2'but accepted as strings by the default detection. - Unicode escapes are parsed leniently
-
\u{}with no digits is accepted and yields U+0000, and more than six hex digits are accepted and wrap around (\u{1000000041}isA). Escapes that produce a surrogate or a code point above U+10FFFF are rejected (see "ENCODING"). - Parsing leaks a few bytes per property
-
When ckdl reports a property it replaces an internal string (the node name or the previous key) without freeing it, so every property of a parsed document leaks one small allocation (about 32 bytes). Documents without properties are not affected. This only matters for long-running processes that parse many documents (about 30 MB per million properties); the fix belongs in ckdl.
- Vertical tab is whitespace, not a newline
-
KDL 2.0.0 lists U+000B as a newline; ckdl treats it as whitespace.
- Special numbers cannot be written as KDL v1
-
KDL 1.0.0 has no spelling for infinity and NaN;
emit_kdldies instead of writing invalid v1. - Number spelling is not preserved
-
Numbers are written in a canonical form:
0xFFcomes back as255,1_000as1000,1e3as1000.0. Only numbers kept as text (kindstring, see "VALUE MODEL" in Text::KDL::XS::Value) keep their digits. ckdl's own float parser is up to one unit in the last place off; the values this module returns are rounded correctly. - Duplicate properties are all written
-
node a=1 a=2is written back asnode a=1 a=2, the way it was read; other implementations writenode a=2. Both parse to the same data.
THREADS
Parser and emitter objects hold C state and are never copied into a new thread: in a thread created while such an object exists, the copy is an unblessed, unusable reference. Create parsers inside the thread that uses them; parse_kdl and emit_kdl can be called from any thread. Document, node and value objects are plain Perl data and are cloned like any other. After fork, each process has its own copy of everything and may use it.
PERFORMANCE NOTES
Parsing from a string is the fastest path: the document is handed to ckdl as a single buffer and each event is converted to Perl objects once. Filehandle and code reference sources cost one Perl callback per chunk. The streaming parser avoids building the tree and is the right tool for very large documents or for extracting a few values from a big file.
Values are created as small blessed hashes; a document with a million values needs about 500 MB as a tree. Use Text::KDL::XS::Parser for anything of that size.
The module requires Perl 5.12 or newer built with 64-bit integers (ivsize 8, the default on 64-bit platforms).
SEE ALSO
- Text::KDL::XS::Cookbook
-
Every KDL feature with KDL and Perl examples, plus recipes.
- Text::KDL::XS::Parser, Text::KDL::XS::Document, Text::KDL::XS::Node, Text::KDL::XS::Value
-
The classes that make up the API.
- Text::KDL::XS::Emitter
-
Internal; documented for completeness.
- Alien::ckdl
-
Builds and provides the ckdl library this module links against; its
alienfilepins the ckdl commit that is compiled. - https://kdl.dev, https://github.com/kdl-org/kdl
-
The KDL specification and reference test suite.
- https://github.com/tjol/ckdl
-
The C library doing the actual parsing and emitting.
AUTHOR
Davenonymous <perl@davenonymous.com>
LICENSE
Copyright (C) 2026 Davenonymous.
This Perl distribution is licensed under the same terms as Perl itself. The bundled ckdl library (linked statically via Alien::ckdl) is MIT-licensed.