Skip to content

API Cheat Sheet

Petrus Pradella edited this page Jun 30, 2026 · 6 revisions

API Cheat Sheet

A one-page reference. See the linked pages for detail.

Open / lifecycle — Lifecycle, Reload & Watching

Config cfg = Config.open(path, codec);   // ABSENT / EMPTY / PARSE_FAILED_BACKED_UP / OK
Config cfg = Config.open("settings.yml");             // codec from the extension (String|File|Path)
Config cfg = Config.open(path, codec, BackStore.Durability.FSYNC);  // OS_CACHE (default) | FSYNC
cfg.save();  cfg.saveIfDirty();  cfg.saveAsync();
cfg.reload();                            // OK / ABSENT / PARSE_FAILED_KEPT
cfg.onReload(runnable).withAutoReload(Duration.ofSeconds(2));  cfg.stopAutoReload();
cfg.withAutoReload(Duration.ofSeconds(2), true);      // also hash content (catch same-size edits)
cfg.close();                             // AutoCloseable
cfg.lastLoadStatus();  cfg.isDivergedFromDisk();  cfg.getLastModified();  cfg.hasBeenModified();

open(String|File|Path) derives the codec from the file-name extension and throws CodecException on a missing/unknown one — it never guesses. Pass a codec to override.

In-memory & cross-format

Config mem = Config.inMemory();          // full typed/POJO API, no file; save() throws
Config raw = new Config();               // no codec at all — native values only (scalar/Map/list/JsonNode)

Config cfg = Config.open("server.yml");
cfg.save(new JsonCodec());               // one-shot persist as JSON; live codec stays YAML
cfg.changeCodec(new TomlCodec());        // switch the format for every subsequent save()

inMemory() binds InMemoryCodec, so setValue(path, pojo), getValue(path, type) and the annotations all work — but there is no text format, so save() throws. save(Codec) writes the tree once in another format to the same file without touching the file name (a later extension-inferred open would pick the name's codec, not the one you saved with).

Set / remove — The Dynamic API

cfg.setValue(path, value);               // null value deletes; auto-vivifies objects
cfg.setValue(path, value, comment);
cfg.removeValue(path);               // boolean

Typed getters

getString  getInt  getLong  getDouble  getBoolean  getStringList  getList  getUUID   // + (path, default) forms
getValue(path)        // unwrapped scalar / node
getNode(path)         // raw JsonNode or null

getString on an object node returns the default (never the raw JSON); on an array it joins the elements with newlines. getUUID(path) and getUUID(path, def) are both tolerant — a malformed or absent value yields null/the default, never throws (like the numeric getters).

Keys / sections

cfg.contains(path);
cfg.getKeys();  cfg.getKeys(path);  cfg.getKeys(path, true /*deep*/);
cfg.getConfigSection(path);          // always a view; use cfg.contains(path) for existence
cfg.getRoot();                       // live ObjectNode escape hatch

The path separator is fixed at .. A key that itself contains a dot is escaped with a backslash, so rates.usd\.brl addresses the single key "usd.brl" under rates; \\ is a literal backslash. The escape is a no-op for ordinary keys.

cfg.getDouble("rates.usd\\.brl");    // reads the key "usd.brl" under "rates" (Java string: \\ → \)

Defaults & comments — Default Values & Comments

cfg.getOrSetValueIfAbsent(path, def);
cfg.getOrSetValueIfAbsent(path, def, comment);   // seeds comment if absent
cfg.setComment(path, text);                     // authoritative (CommentType.BLOCK default)
cfg.setDefaultComment(path, text);              // set-if-absent
cfg.getComment(path);  cfg.getComment(path, CommentType.SIDE);
cfg.setHeader("line1", "line2");  cfg.setDefaultHeader(...);  cfg.getHeader();  cfg.clearHeader();  // file header
cfg.setFooter(...);  cfg.setDefaultFooter(...);  cfg.getFooter();  cfg.clearFooter();               // file footer
cfg.setValueIfAbsent(path, value);  cfg.setValueIfAbsent(path, value, comment);  // void seed
MigrationResult r = cfg.migrateKey(oldPath, newPath);  // moves data + the key's whole comment subtree
// r: MOVED | SAME_PATH | INVALID_ROOT | ALREADY_MIGRATED | SOURCE_ABSENT   (r.moved() for the only mutating case)

migrateKey is safe to run unconditionally on every startup: the returned MigrationResult tells a real move (MOVED) from a benign re-run (ALREADY_MIGRATED, source already moved) and from a likely typo (SOURCE_ABSENT, neither side exists). When both paths exist the source overwrites the target (MOVED).

Binding — Entity Binding

T pojo = cfg.loadAs(Type.class, codec);                 // bind whole tree + @PostLoad
T sub  = cfg.getValue("a.b", Type.class);            // bind a subtree (codec from open())
T sub  = cfg.getValue("a.b", Type.class, codec);    // ... or an explicit codec
EntityBinder<T> b = cfg.bind(Type.class[, codec][, opts]); // b.read(""); b.write("", pojo); b.lastLoadIssues();
cfg.setValue(path, pojo);                               // a POJO setValue MERGES into the tree

BindResult<T> r = cfg.loadAsResult(Type.class, codec);  // also: b.readResult("")
r.value();  r.issues();  r.hasIssues();                 // issues travel with the value

BindOptions.defaults()
    .withCoercion(BindOptions.Coercion.STRICT)          // or LENIENT (default)
    .withObsoletePolicy(BindOptions.ObsoletePolicy.COMMENT_OUT);  // PRESERVE (default) | REMOVE | COMMENT_OUT

getValue's 2-arg form needs a codec from open() (else IllegalStateException); an absent path binds the type's defaults, a root path (""/null) binds the whole tree. COMMENT_OUT keeps the obsolete key but stamps a deprecation block comment — LOSSLESS codecs only (degrades to PRESERVE on a NONE/LOSSY codec).

@KeyIndex collections — @KeyIndex Collections

cfg.setValue(path, collection);                       // auto key-major when the element type has @KeyIndex
List<T> back = cfg.getList(path, Type.class);          // detects the indexed section by node shape
BindResult<List<T>> r = cfg.getListResult(path, Type.class);  // the list + read issues

Annotations — Annotations

@Key(value, transformCase)   @Comment(value, mode)   @Section("a.b")   @KeyIndex   @PostLoad
KeyTransformCase.{NONE, KEBAB_CASE, SNAKE_CASE, CAMEL_CASE, UPPER_CAMEL_CASE}
CommentMode.{OVERRIDE, SET_IF_ABSENT}

@JsonNaming(KeyCaseStrategy.Kebab.class)   // class-wide case (or .Snake); a field @Key overrides it
@JsonNaming(KeyCaseStrategy.Snake.class)

Codecs — Codecs & Formats

new YamlCodec()   // LOSSLESS   yml, yaml
new JsonCodec()   // NONE       json
new TomlCodec()   // LOSSLESS   toml   (no null)
new JsoncCodec()  // LOSSY      jsonc

CodecRegistry.defaults().forFile("a.toml");   // resolve by extension

Clone this wiki locally