Uses only Java built-in APIs. Great for minimal footprint.
| Format | Module | Comments | Errors | Notes |
|---|---|---|---|---|
| XML | π xml | β | β | Uses Java built-in XML APIs |
| Properties | properties | β | β | Flat key=value format |
| INI | properties | β | β | Section-based [section] format |
| Binary | binary | β | β | Java ObjectStream serialization |
| Format | Module | Comments | Errors | Notes |
|---|---|---|---|---|
| YAML | π yaml-snakeyaml | β | β | Via SnakeYAML |
| YAML | yaml-jackson | β | β | Via Jackson (SnakeYAML underneath) |
| TOML | toml-jackson | β | β | TOML 1.0 via Jackson |
| HJSON | hjson | β | β | Human JSON via hjson-java |
| JSON | json-gson | β | β | Via Google GSON |
| JSON | json-jackson | β | β | Via Jackson |
| JSON | json-simple | β | β | Via json-simple, no pretty print |
Special implementations for safe use in specific environments, e.g., game servers.
| Platform | Module | Comments | Errors | Notes |
|---|---|---|---|---|
| Bukkit/Spigot/Paper | π yaml-bukkit | β | β | No extra dependencies (best with okaeri-platform) |
| BungeeCord/Waterfall | π yaml-bungee | β | β | No extra dependencies needed |
| Velocity/Sponge | yaml-snakeyaml or json-gson | Varies | Varies | Exclude format deps when shading (provided by environment) |
Legend:
- Comments:
@Comment/@Headersupport - Errors: Rust-style error markers (see below)
Supported formats provide precise error messages that pinpoint exactly where serdes failed:
error[DurationTransformer]: Cannot transform 'scoreboard.dummy.update-rate' to Duration from String
--> config.yml:1118:18
|
1114 | # How often should all players' dummy be refreshed (regardless of other triggers)
1115 | # Format: <value><unit><value><unit><...>
1116 | # Units: s - seconds, m - minutes, h - hours
1117 | # Example: 1m30s
1118 | update-rate: hello
| ^^^^^ Expected duration (e.g. 30s, 5m, 1h30m, 1d)
error[EnvironmentPlaceholderProcessor]: Cannot pre-process 'database.password' to String from String
--> config.yml:14:13
|
12 | host: ${DB_HOST:localhost}
13 | port: ${DB_PORT:5432}
14 | password: ${DB_PASSWORD}
| ^^^^^^^^^^^^^^ Unresolved property or env
- Jakarta EE: Jakarta EE based with full Jakarta Bean Validation support
- Okaeri Validator: simple validator with Jakarta EE-like annotations but much less code (and features)
- π serdes-commons: for common but not mandatory types, e.g. Instant, Pattern, Duration
- serdes-bukkit: for Minecraft (Bukkit) types
- serdes-bucket4j: for vladimir-bukhtoyarov/bucket4j types
For standalone platforms, the xml module is a great choice with zero external dependencies (uses Java built-in APIs) and full comment support.
Alternatively, the hjson module offers a good balance of readability and small footprint, also including full comment support.
Combine either with Okaeri Validator for a complete config solution.
For any platform, if some form of config validation is applicable (e.g., requiring that an integer is positive), it is recommended to use Okaeri Validator when possible.
Only a few kilobytes but makes for a much better experience for both end-users and developers.
For any platform, if some form of i18n/translation is needed, you may be interested in okaeri-i18n, which can use okaeri-configs as a translation source.
Okaeri's configuration library is an easy way to use Java classes as config adapters:
- Supports different environments with minimal hassle and relatively small footprint
- Allows for even complex types to be serialized/deserialized
- Enhances your configs with durable comments and strongly-typed fields
- Provides the ability to access typed fields with classic getters and setters
- Core library is just ~177kB in size, most of the adapters require only ~100 lines of code
@Header("################################")
@Header("# My Application Config #")
@Header("################################")
public class AppConfig extends OkaeriConfig {
@Comment("Application settings")
private String appName = "MyApp";
private Integer maxConnections = 100;
@Comment("API key from properties (-DAPI_KEY=1234) or environment")
private String apiKey = "${API_KEY}";
// use ${API_KEY:default} for fallback value
// or @Variable("API_KEY") annotation with field value as fallback
@Comment("Server configuration (subconfig)")
private ServerConfig server = new ServerConfig();
@Comment("Database settings (serializable)")
private DatabaseConfig database = new DatabaseConfig("localhost", 5432);
@Comment("Feature flags")
private Map<String, Boolean> features = Map.of(
"experimental", false,
"logging", true
);
@Comment("Allowed IP addresses")
private List<String> allowedIps = List.of("127.0.0.1", "192.168.1.1");
// Subconfig - extends OkaeriConfig, supports nested comments
public static class ServerConfig extends OkaeriConfig {
private String host = "localhost";
private Integer port = 8080;
@Comment("Connection timeout in seconds")
private Integer timeout = 30;
}
// Serializable - lighter weight, no nested comments
@Getter @Setter @AllArgsConstructor @NoArgsConstructor
public static class DatabaseConfig implements Serializable {
private String host;
private Integer port;
}
}Pick your poison.
// recommended
TestConfig config = ConfigManager.create(TestConfig.class, it -> {
it.configure(opt -> {
opt.configurer(new YamlBukkitConfigurer(), new SerdesBukkit()); // specify configurer implementation, optionally additional serdes packages
opt.bindFile(new File(this.getDataFolder(), "config.yml")); // specify Path, File or pathname
opt.removeOrphans(true); // automatic removal of undeclared keys
opt.resolvePlaceholders(); // resolve ${VAR} and ${VAR:default} from environment
});
it.saveDefaults(); // save file if it does not exist
it.load(true); // load and save to update comments/new fields
});TestConfig config = (TestConfig) ConfigManager.create(TestConfig.class)
.configure(opt -> {
opt.configurer(new YamlBukkitConfigurer(), new SerdesBukkit()); // specify configurer implementation, optionally additional serdes packages
opt.bindFile(new File(this.getDataFolder(), "config.yml")); // specify Path, File or pathname
opt.removeOrphans(true); // automatic removal of undeclared keys
opt.resolvePlaceholders(); // resolve ${VAR} and ${VAR:default} from environment
})
.saveDefaults() // save file if it does not exist
.load(true); // load and save to update comments/new fieldsTestConfig config = new TestConfig();
config.configure(opt -> {
opt.configurer(new YamlBukkitConfigurer(), new SerdesBukkit()); // specify configurer implementation, optionally additional serdes packages
opt.bindFile(new File(this.getDataFolder(), "config.yml")); // specify Path, File or pathname
opt.removeOrphans(true); // automatic removal of undeclared keys
opt.resolvePlaceholders(); // resolve ${VAR} and ${VAR:default} from properties/environment
});
config.saveDefaults(); // save file if it does not exist
config.load(true); // load and save to update comments/new fields- Subconfigs: Classes extending
OkaeriConfig- supports nested comments and full config features - Serializable: Classes implementing
Serializable- lighter weight alternative to subconfigs
- Primitives:
boolean,byte,char,double,float,int,long,short - Wrappers:
Boolean,Byte,Character,Double,Float,Integer,Long,Short,String - Math:
BigInteger,BigDecimal - Enums: Automatically serialized via
name()with case-insensitivevalueOf()deserialization
- List:
List<T>βArrayList<T> - Set:
Set<T>βLinkedHashSet<T> - Map:
Map<K, V>βLinkedHashMap<K, V> - Custom implementations supported if non-interface type with default constructor
- Use
ObjectSerializerfor complex serialization logic - Use
ObjectTransformerfor simple type conversions - See platform-specific serdes modules for examples (bukkit, commons, etc.)