Multi-Format Apps

Use this guide when one application should accept several file formats while keeping business logic independent from the chosen backend.

Add the Backends

dependencies {
  implementation(platform("net.pistonmaster:pistonconfig-bom:0.1.0-SNAPSHOT"))
  implementation("net.pistonmaster:pistonconfig-core")
  implementation("net.pistonmaster:pistonconfig-yaml")
  implementation("net.pistonmaster:pistonconfig-toml")
  implementation("net.pistonmaster:pistonconfig-json")
  implementation("net.pistonmaster:pistonconfig-hocon")
  implementation("net.pistonmaster:pistonconfig-properties")
}

Install only the backends you actually support. The core model stays the same after a file is loaded.

Register Formats

static final List<ConfigFormat> FORMATS = List.of(
  YamlConfigFormat.INSTANCE,
  TomlConfigFormat.INSTANCE,
  JsonConfigFormat.INSTANCE,
  HoconConfigFormat.INSTANCE,
  PropertiesConfigFormat.INSTANCE
);

Keep format selection near file I/O. Application code should receive ConfigDocument, not a parser-specific object.

Select a Loader

static ConfigLoader loaderFor(Path path) {
  var extension = extension(path.getFileName().toString());
  return FORMATS.stream()
    .filter(format -> format.extensions().contains(extension))
    .findFirst()
    .orElseThrow(() -> new ConfigException("Unsupported config extension: " + extension))
    .loader();
}

static String extension(String fileName) {
  var index = fileName.lastIndexOf('.');
  return index < 0 ? "" : fileName.substring(index + 1).toLowerCase(Locale.ROOT);
}

Use ConfigFormat.extensions() instead of duplicating extension lists across your application.

Keep Defaults Format-Neutral

var defaults = ConfigDocument.empty()
  .set("server.host", "0.0.0.0")
  .set("server.port", 25565)
  .set("features.metrics", true);

Defaults can come from manual construction, annotations, static fields, or another loaded file. They merge through the same API.

Convert Between Formats

var sourceLoader = loaderFor(Path.of("config.yml"));
var targetLoader = loaderFor(Path.of("config.toml"));
var document = ConfigLoaders.load(Path.of("config.yml"), sourceLoader);

ConfigLoaders.save(Path.of("config.toml"), targetLoader, document);

Format-specific metadata remains inspectable in the document, but a target backend can only write details it supports.

Layer Owns
File boundary loader selection, path validation, save location
Config workflow migrations, default merging, overrides
Application mapping codecs, annotations, static properties, manual reads
Business logic typed application values only

This keeps parser differences from leaking into application behavior.

Search Documentation