BoxLang 🚀 A New JVM Dynamic Language Learn More...

cfTOML

v1.2.1 Modules

cfTOML

Pure CFML library for parsing and emitting TOML 1.0.0. Cross-engine compatible across Adobe ColdFusion 2016+, Lucee 5+, and BoxLang 1+.

ForgeBox Version ForgeBox Downloads

Install

cfTOML is a single CFC file. Drop cfTOML.cfc into your project and use it. No external dependencies, no Java JARs, nothing to compile.

Source: https://github.com/JamoCA/cfTOML

Option 1: Download a release ZIP

Go to https://github.com/JamoCA/cfTOML/releases, download the latest release, and unzip it anywhere on your CFML path. Only cfTOML.cfc is required at runtime; the rest of the files are tests, examples, and tooling.

Option 2: Clone the repository

git clone https://github.com/JamoCA/cfTOML.git

Then copy cfTOML.cfc to your project, or add a CFML mapping pointing at the clone directory. In your Application.cfc:

this.mappings["/cftoml"] = expandPath("./lib/cfTOML");

Then instantiate:

parser = new cftoml.cfTOML();

Option 3: Git submodule

If your project is in git, add cfTOML as a submodule:

git submodule add https://github.com/JamoCA/cfTOML.git lib/cfTOML
git submodule update --init --recursive

Pin to a specific release tag for reproducible builds:

cd lib/cfTOML
git checkout v1.2.0
cd ../..
git add lib/cfTOML
git commit -m "pin cfTOML to v1.2.0"

Install via ForgeBox (CommandBox users)

If you already use CommandBox / ForgeBox:

box install cftoml

Or in your box.json:

"dependencies": {
    "cftoml": "^1.2.0"
}

Quick start

parser = new cfTOML();

// Parse from string
data = parser.tomlDeserialize(fileRead("config.toml"));

// Or read directly from file
data = parser.tomlReadFile("config.toml");

writeOutput(data.server.host);  // "10.0.0.1"

// Emit
toml = parser.tomlSerialize(["server": ["host": "10.0.0.1", "port": 8080]]);

// Or write directly to file
parser.tomlWriteFile("config.toml", data);

TOML version support

cfTOML supports both TOML 1.0.0 (default) and TOML 1.1.0 (opt-in). Pass spec = "1.1.0" to parse a 1.1.0 document:

var data = tomlDeserialize(content, ["spec": "1.1.0"]);

By default, the emitter produces 1.0.0-compatible output even when the parser is in 1.1.0 mode. This means you can safely parse 1.1.0 inputs and re-emit them for consumers that only support 1.0.0. To emit 1.1.0-only constructs, set the corresponding option:

Construct Option
Multi-line inline tablesinlineMultiline = true (switches when single-line exceeds 80 characters or contains multi-line strings)
\e and \xHH escapesuseExtendedEscapes = true
Omit :00 seconds in datetimesomitZeroSeconds = true
All-digit bare keys (e.g. 1234 = "value")useBareDigitKeys = true

Setting any of these knobs while spec = "1.0.0" throws cfTOML.ConfigError.

Conformance

cfTOML is tested against the BurntSushi/toml-test corpus (pinned at toml-test v1.5.0, manifest-filtered per spec) for both spec versions. On engines with native case-sensitive struct support (Adobe ColdFusion 2021, 2023, 2025):

  • TOML 1.0.0: 182/182 valid (100%) + 371/371 invalid (100%), zero runtime errors
  • TOML 1.1.0: 187/187 valid (100%) + 361/361 invalid (100%), zero runtime errors

On engines where case-sensitive ordered structs are not available or whose dot-notation accessor uppercases (Adobe CF 2016, Lucee 5/6/7, BoxLang 1.13), the conformance numbers are 180/182 + 371/371 (1.0.0) and 185/187 + 361/361 (1.1.0). The 2-test difference is the BurntSushi case-sensitive test (valid/key/case-sensitive.toml) and valid/inline-table/key-dotted-4.toml, which require keys differing only in case to round-trip distinctly. See the "Known limitations" section for context.

The unit suite passes 634/634 on every supported engine. Every valid TOML input parses into the correct typed structure, and every invalid input is rejected with a cfTOML.*-prefixed exception. Strict-mode coverage includes control characters in strings, leading-zero integers, case-sensitive true/false/inf/nan, separator state machines for arrays and inline tables, trailing-token rejection after statements, datetime offset range validation, multiline-string line-continuation rules, bare-key segment validation in table headers, table-conflict tracking for dotted-key intermediates and array-of-tables, inline-table immutability, and strict UTF-8 byte-level validation.

(Run tests/conformance/run-conformance.cfm?spec=1.0.0 or ?spec=1.1.0 to reproduce.)

Parser features added in 1.1.0

  • Multi-line inline tables - newlines and trailing commas inside { ... }.
  • \e escape - decodes to U+001B (ESC).
  • \xHH escape - exactly two hex digits, decodes to U+00HH.
  • Optional seconds in datetimes - 1979-05-27T07:32 is equivalent to 1979-05-27T07:32: 00.
  • All-digit bare keys - 1234 = "value" is permitted; the key is always a string.

Public API

tomlDeserialize(toml, options)

Parse a TOML string and return an ordered struct.

  • toml (required string): TOML source.
  • options (struct, default [:]): see Options below.

Returns: ordered struct.

tomlReadFile(path, options)

Read a TOML file (UTF-8) and parse it.

  • path (required string): absolute or webroot-relative file path.
  • options (struct): same as tomlDeserialize.

Returns: ordered struct.

tomlSerialize(data, options)

Serialize an ordered struct to a TOML 1.0 string.

  • data (required struct): the data to serialize.
  • options (struct): see Options below.

Returns: TOML string.

tomlWriteFile(path, data, options)

Serialize and write to a UTF-8 file (no BOM).

  • path (required string): output file path.
  • data (required struct): data to write.
  • options (struct): same as tomlSerialize.

Returns: void.

Options

Key Type Default Purpose
strict booleantrue Reject any spec violation. false allows trailing commas in inline tables.
dateTimeReturn string"cfdate" How datetime values are returned: "cfdate" (CFML date object, milliseconds preserved; offset datetimes are converted to the server's local timezone so the instant is preserved), "iso8601" (raw RFC 3339 string), "javatime" (java.time.OffsetDateTime / LocalDateTime / LocalDate / LocalTime).
int64Mode string"double" Integer return type: "double" (CFML number, max safe 2^53), "javalong" (Java Long, full int64 range), "string" (decimal digit string for arbitrary range).
indent string"" Emit-side: indent string under nested [header] blocks. "" = flat output, "\t" = one-tab indent per depth.
sortKeys booleanfalse Emit-side: alphabetize keys. Default preserves insertion order.
inlineThreshold numeric0 Emit-side: if >0, top-level structs with <=N scalar-only keys emit as inline tables instead of [header] blocks.
onNull string"skip" Emit-side null handling: "skip" (omit key), "throw" (cfTOML.TypeError), "emptyString" (key = "").
queryAsArrayOfTables booleanfalse Emit-side: when true, CFML query objects emit as array-of-tables. When false, query values throw cfTOML.TypeError.

Datetime modes

TOML defines four distinct datetime types: offset, local, date-only, time-only. CFML's native date type doesn't cleanly distinguish these, so the parser offers three return modes via dateTimeReturn:

  • "cfdate" (default): All four types return CFML date objects. Offset datetimes (including Z) are converted to the server's local timezone via java.time.ZoneId.systemDefault() so the returned datetime represents the same INSTANT as the source; wall-clock varies by server zone, the instant does not. Local datetimes (no offset in source) keep their wall-clock verbatim. Fractional seconds are truncated to milliseconds (3 digits) and applied with dateAdd("l", ms, dt) so precision survives on CF2016+ where createDateTime() lacks a millisecond argument. Ergonomic for code that uses CFML's date functions.
  • "iso8601" : All four types return the original RFC 3339 string verbatim. Lossless. Best for config-file use cases.
  • "javatime" : Returns typed java.time.* objects (OffsetDateTime, LocalDateTime, LocalDate, LocalTime). Lossless, type-distinct, and zone-aware. Requires Java method calls on the caller side.

Emit key ordering

When serializing CFML to TOML, root-level scalars and arrays get written before the first [header] block. This is the TOML grammar at work, not cfTOML being clever.

In TOML, every bare key after a [header] belongs to that header's table until the next header shows up. So a CFML struct like { "server": { "host": "..." }, "tags": [...] } cannot serialize as:

[server]
host = "..."
tags = [...]

That would round-trip back as server.tags, which is the wrong shape. cfTOML writes tags above [server] so the original structure survives:

tags = [...]

[server]
host = "..."

Every TOML emitter (Rust, Python, Go) does the same. The data round-trips exactly. Only the textual order of keys at the root differs from the source. To keep the source order more visible, pass inlineThreshold so small structs stay inline (server = { host = "..." }) and live at the root instead of becoming [header] blocks.

Errors

The library throws four distinct exception types:

  • cfTOML.ParseError - syntax violations. detail is JSON with line, column, offset, snippet, expected keys.
  • cfTOML.TypeError - value-shape mismatches (redefining a table as a scalar, unflagged query on emit, etc.).
  • cfTOML.DuplicateKeyError - key redefinition.
  • cfTOML.OverflowError - int64 out of range when int64Mode = "double" and strict = true.

Engine support

Engine Status
Adobe ColdFusion 2016Supported
Adobe ColdFusion 2021Supported
Adobe ColdFusion 2023Supported
Adobe ColdFusion 2025Supported
Lucee 5Supported
Lucee 6Supported
Lucee 7Supported
BoxLang 1+Supported

See tools/run-engine-matrix.ps1 to run the full unit test suite across all engines (requires CommandBox).

TOML conformance

The library is verified against the BurntSushi/toml-test conformance corpus for both TOML 1.0.0 and 1.1.0 across the full engine matrix (Adobe CF 2016/2021/2023/2025, Lucee 5/6/7, BoxLang 1.13). Adobe CF 2021+ passes 100% valid and 100% invalid (553/553 for 1.0.0 and 548/548 for 1.1.0), zero runtime errors. CF 2016, Lucee, and BoxLang pass 551/553 (1.0.0) and 546/548 (1.1.0); the two-test gap on each spec is the case-sensitive-key tests that require an engine-native case-sensitive struct with working dot-notation. See tests/conformance/README.md for instructions on fetching and running the conformance suite.

All malformed inputs throw a cfTOML.*-prefixed exception (ParseError, TypeError, DuplicateKeyError, OverflowError, or ConfigError). Engine-native exceptions from parseDateTime(), java.time.*.parse(), or chr() on out-of-range Unicode no longer leak through.

Known limitations

  • Comment preservation across parse/emit is out of scope (per TOML library convention - revisit as cfTOML-edit in a future release).
  • Case-sensitive ordered struct support is currently Adobe CF 2021+ only. CF 2016/2018, Lucee 5/6/7, and BoxLang either lack the type entirely or implement it in a way that defeats CFML's dot-notation accessor (Lucee/BoxLang's ordered-casesensitive struct returns a wrapper whose dot-notation accessor uppercases the lookup key, so a key stored as section cannot be read via data.section). On those engines the parser falls back to the case-insensitive ordered [:] literal, so a top-level pair of keys differing only in case (section and sectioN) collide. Bracket-notation access (data["section"]) works regardless.
  • IEEE-754 precision limits affect integers above 2^53 in int64Mode = "double" (the default). Use "javalong" or "string" for full 64-bit range.
  • CFML date objects are emitted as DATETIME_LOCAL (zone-naive). For specific offsets, pass a java.time.OffsetDateTime instance.
  • Sub-second precision is preserved at millisecond level for java.time.* datetime values.

Live demo

After cloning, start a local CFML server and open demo.cfm in a browser. The demo shows the engine and version it detected, lets you paste or pick a TOML payload to parse, lets you pick a pre-built CFML object (or paste JSON) to serialize, and reports both single-conversion time and the number of conversions per second.

If cf_dump by kwaschny is available, the demo also renders the parsed object through it below the JSON output. The native <cfdump> tag collapses line breaks inside strings and omits data types on scalars, so cf_dump gives a clearer picture of what came out of the parser.

git clone https://github.com/JamoCA/cfTOML.git
cd cfTOML
box server start

Then open http://localhost:8128/demo.cfm

Development

git clone https://github.com/JamoCA/cfTOML.git
cd cfTOML
box server start
curl http://localhost:8128/tests/runner.cfm

To run the conformance suite:

powershell tests/conformance/fetch-corpus.ps1
curl http://localhost:8128/tests/conformance/run-conformance.cfm

To run the engine matrix:

powershell tools/run-engine-matrix.ps1

License

MIT. See LICENSE.

Author

James Moberg [email protected]

Changelog

[1.2.1] - 2026-05-14

Fixed

  • README ForgeBox version and download badges pointed at the wrong package slug (toml instead of cftoml), so both badges showed "package not found" on the rendered README. The badge URLs and the linked package pages now use the correct cftoml slug.

[1.2.0] - 2026-05-12

Added

  • Opt-in TOML 1.1.0 parser support via the spec option. Default remains "1.0.0"; pass { spec: "1.1.0" } to enable. New 1.1.0 parser features:
    • Multi-line inline tables (newlines, comments, and trailing commas inside { ... })
    • \e escape sequence (U+001B)
    • \xHH escape sequence (exactly two hex digits, U+00HH)
    • Optional seconds in datetime values (HH:MM accepted, synthesized to HH:MM: 00)
    • Non-ASCII bare keys (Unicode letters and digits inside table headers and key paths)
  • Four emitter knobs for opting into 1.1.0-only output: inlineMultiline, useExtendedEscapes, omitZeroSeconds, useBareDigitKeys. Default behavior even in spec = "1.1.0" mode is 1.0.0-compatible output unless these knobs are set, so callers can safely parse 1.1.0 inputs and re-emit them for 1.0.0 consumers.
  • cfTOML.ConfigError raised when a 1.1.0-only emitter knob is set under spec = "1.0.0".
  • BurntSushi/toml-test 1.1.0 corpus driver. Run tests/conformance/run-conformance.cfm?spec=1.1.0.
  • Case-sensitive ordered struct support on engines that provide it (Adobe CF 2021+, BoxLang, Lucee). The parser probes structNew("ordered-casesensitive") (and Lucee's linked-casesensitive) at init time and uses it for the parsed data tree, so [section] and [Section] no longer collide. CF 2016/2018 keep the case-insensitive [:] fallback.

Fixed

  • All malformed inputs now throw a cfTOML.*-prefixed exception. Datetime range violations (out-of-range hour, mday, month, etc.), Unicode escape out-of-range and surrogate-range violations, and non-UTF-8 file encodings previously surfaced as engine-native Expression, Application, or Template exceptions. They are now wrapped as cfTOML.ParseError.
  • Top-level and inline-table key positions accept quoted-string segments (STRING_BASIC and STRING_LITERAL). Previously, a key like "foo.bar" = 1 or {"key" = "value"} parsed as a quoted-value mistake. TOML 1.0.0 has always allowed quoted keys; the parser now matches the spec.
  • Mixed quoted/bare dotted key paths like "name".'last' = "Dent" parse correctly.
  • Empty quoted segments ("", '') survive dotted-path splitting and table-header parsing. [""], ''.x, and "".'' = "x" are valid TOML 1.0.0 patterns.
  • Whitespace around dot separators in dotted paths is now trimmed correctly ([ j . "Êž" . 'l' ] produces ["j", "Êž", "l"], not ["j", "Êž ", " 'l'"]).
  • Tokens like inf, nan, true, false are accepted as bare keys when they appear in key position (the lexer greedily emits FLOAT/BOOL for them, but the parser now treats those token types as valid key segments).
  • Bare keys can start with - (-key = 1, [-], [---] parse correctly).
  • FLOAT-shaped lexemes like 10e3 and 3.14 are accepted as keys when at start-of-statement (3.14 = "pi" parses as dotted key 3.14).
  • Trailing-dash lookahead on INT lexing: 2000-datetime = "x" now lexes as a single bare key rather than splitting into INT(2000) and KEY(-datetime).
  • \u and \U escape decoders explicitly reject the surrogate range (U+D800..U+DFFF) and (for \U) codepoints above U+10FFFF. High codepoints (U+10000+) now produce correct UTF-16 surrogate-pair output via Character.toChars().
  • All-digit bare keys (1234 = "value") are now accepted in TOML 1.0.0 mode. The spec note says "bare keys are allowed to be composed of only ASCII digits, e.g. 1234, but are always interpreted as strings"; an earlier internal misreading had gated this behind 1.1.0.
  • The \u, \U, and \xHH escape decoders use Java's Character.toChars() for the resulting codepoint instead of CFML's chr(). chr(0) on Adobe CF returns an empty string, which silently dropped the NUL character from inputs like "�"; the Java path preserves the full Unicode scalar across the entire range U+0000..U+10FFFF.
  • The conformance runner's typed-JSON helper uses structNew("ordered-casesensitive") (with engine fallbacks) instead of [:], so test data with keys differing only in case no longer merges before comparison.
  • The conformance runner reads expected JSON via Jackson (com.fasterxml.jackson.databind.ObjectMapper) instead of CFML's deserializeJSON(), so the expected struct stays case-sensitive too.
  • The typed-JSON helper checks java.lang.String class before the numeric-looking-string fallback, so a quoted TOML scalar like version = "4" is reported as a string instead of being silently promoted to integer.
  • Double precision: the typed-JSON helper uses java.lang.Double.toString(d) instead of CFML's toString(), which on Adobe CF truncates to roughly 11 significant digits. Full IEEE-754 precision now survives serialization for comparison.

Changed

  • The conformance fetcher (tests/conformance/fetch-corpus.ps1) now applies the toml-test files-toml-1.0.0 manifest to the 1.0.0 corpus, matching how it already filtered the 1.1.0 corpus. The previous unfiltered copy left 1.1.0-only valid tests in the 1.0.0 set, which skewed the 1.0.0 numbers downward. Re-run the fetcher after upgrading to pick up the filter.
  • dateTimeReturn = "cfdate" (default) is no longer zone-lossy on offset datetimes. Previously the parser dropped the zone and returned the raw wall-clock from the source, so 1979-05-27T07:32: 00Z and 1979-05-27T07:32: 00-08:00 both produced the CFML datetime 1979-05-27 07:32: 00 regardless of where the server ran. The parser now converts offset datetimes to the server's local zone via java.time.OffsetDateTime.atZoneSameInstant(ZoneId.systemDefault()), so the returned CFML datetime represents the same instant as the source. Local datetimes (no offset in source) still keep their wall-clock verbatim because there's no zone to interpret. Code that relied on the old zone-dropping behavior should switch to dateTimeReturn = "iso8601" (raw RFC 3339 string) or "javatime" (zone-aware java.time.* objects).
  • Fractional-second precision is preserved in cfdate mode. Sources like 1979-05-27T07:32: 00.456 and 07:32: 00.7Z now carry their milliseconds through to the returned CFML date via dateAdd("l", ms, dt). Beyond three digits the fraction is truncated; shorter fractions right-pad (.7 -> 700ms). CFML's native createDateTime() lacks a millisecond argument on CF2016, which is why the dateAdd workaround is used uniformly.
  • The emitter builds the DATETIME_LOCAL string from year/month/day/hour/minute/second accessors rather than dateTimeFormat() masks because CFML's mask vocabulary is engine-dependent (Adobe CF and Lucee: mm is month, nn is minute; BoxLang: mm is minute, nn is unknown). Component-based emission is identical on every supported engine.
  • Whole-number Doubles emit as TOML integers (1 rather than 1.0). On Lucee, every numeric literal is stored as java.lang.Double regardless of whether the source was 1 or 1.0, so emit-side distinction between integer and float is not portable. Callers who need a TOML float in the output should pass a non-whole value or use int64Mode = "string" with a decimal-shaped string.
  • The emitter calls formatter.format(value) rather than value.format(formatter) on java.time.* objects. BoxLang shadows the CFML format(value, mask) built-in over the Java instance method, so the second form is rejected with a "format mask must be string" error. Calling the formatter as the receiver bypasses the shadowing on every engine.
  • The conformance runner reads files via java.io.FileInputStream + InputStreamReader with a strict UTF-8 decoder rather than ByteBuffer.wrap(byte[]) + decoder.decode(). BoxLang cannot dispatch the static wrap() method on the abstract ByteBuffer class via createObject; the InputStreamReader path uses instance methods only and works everywhere.
  • The runner's per-spec corpus selection uses a plain if/else block instead of a ternary, and the valid/invalid test loops are extracted into named functions. BoxLang's bytecode generator reports "Bad return type" on a mix of template-scope ternaries and large <cfif>-equivalent then-blocks; the function-and-block refactor resolves the verification failure without affecting other engines.
  • tests/Application.cfc and tests/conformance/Application.cfc compute the /cfTOML mapping target from getDirectoryFromPath(getCurrentTemplatePath()) & ".." instead of expandPath(".."). BoxLang resolves expandPath("..") from a sub-template differently than Adobe CF and Lucee, putting the mapping one directory too high and making new cfTOML.cfTOML() fail to resolve.

Conformance

Measured across the full engine matrix (Adobe CF 2016/2021/2023/2025, Lucee 5/6/7, BoxLang 1.13) against the BurntSushi/toml-test corpus (pinned at toml-test v1.5.0, manifest-filtered per spec):

  • TOML 1.0.0 on Adobe CF 2021/2023/2025: 182/182 valid (100%) + 371/371 invalid (100%), 0 runtime errors
  • TOML 1.1.0 on Adobe CF 2021/2023/2025: 187/187 valid (100%) + 361/361 invalid (100%), 0 runtime errors
  • TOML 1.0.0 on Adobe CF 2016, Lucee 5/6/7, BoxLang 1.13: 180/182 valid + 371/371 invalid, 2 expected errors
  • TOML 1.1.0 on Adobe CF 2016, Lucee 5/6/7, BoxLang 1.13: 185/187 valid + 361/361 invalid, 2 expected errors

The 2 errors on the case-insensitive engines are valid/key/case-sensitive.toml and valid/inline-table/key-dotted-4.toml, which require keys differing only in case to round-trip distinctly. Adobe CF 2021+ provides structNew("ordered-casesensitive") that preserves case under dot-notation access; older Adobe and the other engines either lack the struct type or implement it in a way that breaks dot-notation, so the parser falls back to the case-insensitive ordered [:] literal. The unit suite passes 634/634 on every supported engine.

Every valid TOML input parses into the correct typed structure, and every invalid input is rejected with a cfTOML.*-prefixed exception. Strict-mode work that closed the remaining invalid-side gap: control-character rejection across the five string/comment lexer sites, leading-zero integer and float rejection, case-sensitive true/false/inf/nan, array and inline-table separator state machines (rejecting [,], [1,,2], [1 2], {x=3 y=4}), trailing-token rejection after statements ([error] this = "...", a = 1 b = 2), datetime offset hour/minute range validation, multiline-string line-continuation strictness (\<space>x is invalid), bare-key segment validation in table headers (rejecting [name=bad], [a[b], [key#group], [invalid key]), table-conflict tracking for dotted-key intermediates ([fruit]; apple.color="red"; [fruit.apple] now throws), static-array vs array-of-tables distinction in header walks, KV-pair dotted-key rejection when walking through AoT or another header's explicit table, inline-table immutability (rejecting {a = {b=1}, a.c = 2}), and strict UTF-8 byte-level validation in the conformance runner via java.nio.charset.StandardCharsets.UTF_8.newDecoder().onMalformedInput(REPORT).

Notes

  • Library version (1.2.0) is independent of TOML spec version. v1.2.0 supports both TOML 1.0.0 (default) and TOML 1.1.0 (opt-in). Future major versions may change the default; library version increments separately from spec adoption.
  • Skipped library version 1.1.0 to avoid implying the library is locked to a specific TOML spec version.
  • Cargo (the largest TOML consumer) has not adopted 1.1.0 yet. The default remains 1.0.0 for that reason.

1.0.0 (2026-05-11)

Initial release. Full TOML 1.0.0 parser and emitter with round-trip semantic equivalence.

Phase summary

  • Phase 1: Project foundation, framework-less test harness, tokenizer for the easy 80% of TOML lexical features (bare keys, decimal integers, booleans, single-line basic strings, comments, newlines, position tracking).
  • Phase 2: Tokenizer extensions for all scalar value lexemes (literal strings, multi-line basic/literal strings, hex/octal/binary integers, floats with exponent and special values, all four datetime variants) plus decode helpers (decodeBasicStringEscapes, decodeMultiLineBasicEscapes, parseIntegerLexeme, parseRFC3339).
  • Phase 3: Parser MVP - top-level KV pairs, [table] headers, dotted keys, duplicate-key detection, table-redefinition detection. tomlDeserialize and tomlReadFile wired end-to-end.
  • Phase 4: Container tokens ([, ], ,, {, }), bracket-depth tracking, array parsing, inline tables (with immutability), array-of-tables (with conflict detection), quoted-segment paths.
  • Phase 5: Emitter (tomlSerialize, tomlWriteFile). Three-pass table walk (scalars, nested [a.b], AoT [[a.b]]). Options: sortKeys, indent, inlineThreshold, onNull, queryAsArrayOfTables. Round-trip semantic equivalence verified.
  • Phase 6: BurntSushi/toml-test conformance corpus integration, 10-engine matrix scripting, full documentation, ForgeBox publish prep.

Public API

  • tomlDeserialize(toml, options) - parse string to ordered struct
  • tomlReadFile(path, options) - read and parse UTF-8 TOML file
  • tomlSerialize(data, options) - serialize struct to TOML string
  • tomlWriteFile(path, data, options) - serialize and write to file

Supported engines

Adobe ColdFusion 2016/2021/2023/2025, Lucee 5/6/7, BoxLang 1+.

$ box install cftoml

No collaborators yet.
     
  • {{ getFullDate("2026-05-14T11:52:13Z") }}
  • {{ getFullDate("2026-05-14T12:00:01Z") }}
  • 18
  • 1