A simple hierarchical scoped group based key:value storage/configuration format.
An ezconf file follows the following syntax:
group1 {
subgroup1 {
key: value;
key2: \:value\}escaped\;;
key3: key
is
multiline;
key4: "has literal \"";
}
}
group2 {
key: value;
}
Groups can be nested arbitrarily deep.
-
- Groups can be nested
- Only groups can contain key:value pairs
- Group identifier names must be unique in the given scope
- Group identifier names can only contain letters and digits, whitespace is not permitted and will be skipped. (Example:
t est {}
will be parsed astest
.) - Group identifiers cannot be multiline or blank
-
- Keys can contain whitespace but will be trimmed (i.e.
k ey : value;
will be parsed ask ey
) - Keys cannot be multiline or blank
- Keys must be unique in a given scope
- Null keys cannot exist and are invalid
- Special symbols must be escaped with
\
, to escape the escape do this\\
(value literal must not be escaped for keys)
- Keys can contain whitespace but will be trimmed (i.e.
-
- Values can contain whitespace and can be multiline but cannot be blank
- Values will be trimmed at each line while parsing (i.e.
value \n stop;
will be parsed asvalue\nstop
) - Null values do not exist and are invalid
- Special symbols must be escaped with
\
, to escape the escape do this\\
- Literals allow to not escape special characters (exception:
"
)- Literals must have the first non-whitespace character start with
"
- An unescaped
"
will trigger an exception if the first non-whitespace character is not"
- Within the literal
"
must still be escaped - A closing literal still requires the
;
- Literals must have the first non-whitespace character start with
-
Parser automatically escaped key / value pairs on dump
{ -> begin group
} -> end group
: -> key value seperator
; -> end value
# -> comment, skip everything after for this line
" -> value literal
A reference implementation is shipped with this release and JavaDoc is available for all publicly accessible code.
import java.util.Optional;
import de.mynttt.ezconf.Configuration;
import de.mynttt.ezconf.ConfigurationBuilder;
import de.mynttt.ezconf.ConfigurationGroup;
import de.mynttt.ezconf.ConfigurationValidator;
import de.mynttt.ezconf.DefaultValidators;
import de.mynttt.ezconf.EzConf;
import de.mynttt.ezconf.ConfigurationValidator.ValidationContext;
public class Example {
public static void main(String[] args) {
Configuration conf = EzConf.defaultParser().parse("example { key: true; nested { key: value; }}");
System.out.println("Check if group exists? : " + conf.groupExists("example"));
ConfigurationGroup nested = conf.getGroup("example.nested");
System.out.println("Check if value exists? : " + nested.keyExists("key"));
System.out.println("Access values: " + nested.getValue("key") + " or " + conf.getValue("example.nested#key"));
Optional<String> value = conf.findValue("example#key");
for(var entry : nested)
System.out.println(entry.getKey() + ": " + entry.getValue());
Configuration built = ConfigurationBuilder.newDefaultInstance()
.addRoot("root")
.addChild("child")
.put("key", "value")
.endChild()
.endRoot()
.build();
System.out.println("Printing with literals used for escaping:");
System.out.println(EzConf.defaultParser().dumpPretty(built));
System.out.println("Printing without literals used for escaping:");
System.out.println(EzConf.defaultParser(ParserFlags.DUMP_ESCAPE_INSTEAD_OF_LITERAL).dumpPretty(built));
ValidationContext ctx = ConfigurationValidator.newInstance()
.requireGroups("example", "example.nested")
.valuesMatchInGroup(DefaultValidators.IS_BOOLEAN, "example")
.build();
System.out.println("Is valid? : " + ctx.validate(conf).isValid());
ValidationContext ctx2 = ConfigurationValidator.newInstance()
.valuesMatchRecursively(DefaultValidators.IS_BYTE, "example")
.build();
System.out.println("Is invalid? : " + !ctx2.validate(conf).isValid());
}
}