Format Backends
Format modules translate parser-specific documents into the core ConfigDocument model. Choose a backend based on what your users edit and what source detail you need to preserve.
Backend Matrix
| Format | Module | Parser library | Best fit |
|---|---|---|---|
| YAML | pistonconfig-yaml |
SnakeYAML | Human-edited config with rich comments and styles. |
| TOML | pistonconfig-toml |
Night Config | Structured app config with tables and predictable syntax. |
| HOCON | pistonconfig-hocon |
Lightbend Config | Config with substitutions and HOCON-style object syntax. |
| JSON / JSONC / JSON5 | pistonconfig-json |
json5-java | JSON-shaped config with optional comments and JSON5 features. |
| Properties | pistonconfig-properties |
Apache Commons Configuration | Flat key-value files and legacy Java ecosystem config. |
Create a Loader
ConfigLoader loader = YamlConfigFormat.INSTANCE.loader();
var document = ConfigLoaders.load(Path.of("config.yml"), loader);
Every format module exposes a singleton ConfigFormat with a name, file extensions, capabilities, and loader.
Switch by Extension
static ConfigLoader loaderFor(Path path) {
var fileName = path.getFileName().toString();
if (fileName.endsWith(".yml") || fileName.endsWith(".yaml")) {
return YamlConfigFormat.INSTANCE.loader();
}
if (fileName.endsWith(".toml")) {
return TomlConfigFormat.INSTANCE.loader();
}
if (fileName.endsWith(".conf") || fileName.endsWith(".hocon")) {
return HoconConfigFormat.INSTANCE.loader();
}
if (fileName.endsWith(".json") || fileName.endsWith(".jsonc") || fileName.endsWith(".json5")) {
return JsonConfigFormat.INSTANCE.loader();
}
if (fileName.endsWith(".properties")) {
return PropertiesConfigFormat.INSTANCE.loader();
}
throw new IllegalArgumentException("Unsupported config format: " + fileName);
}
Keep backend selection at your boundary. After loading, most operations should work against ConfigDocument.
What Gets Preserved
| Source detail | YAML | TOML | HOCON | JSON module | Properties |
|---|---|---|---|---|---|
| Leading comments | yes | yes | yes | yes | yes |
| Inline comments | yes | backend-dependent | backend-dependent | comment model | no |
| Lists | yes | yes | yes | yes | repeated keys |
| Nested objects | yes | yes | yes | yes | dotted paths |
| Scalar style | yes | limited | limited | number radix and timestamps | separator/layout attributes |
| Source locations | yes | limited | origin line | limited | limited |
Backend-Specific Metadata
Use backend metadata only when code genuinely needs format-specific detail:
var node = document.find("server.port").orElseThrow();
node.metadata(JsonMetadataKeys.NUMBER_RADIX)
.ifPresent(radix -> log.debug("JSON5 radix: {}", radix));
For behavior that should work across formats, prefer core fields such as comment(), decorations(), kind(), and rawValue().
Saving
ConfigLoaders.save(Path.of("config.yml"), loader, document);
Saving is format-aware. A backend writes the source details it can represent and ignores the rest. That is why the core model separates common decorations from backend-specific metadata.