-
-
Notifications
You must be signed in to change notification settings - Fork 68
Object Mapper
Can only create objects that have no-arg constructor, otherwise restricted to populating already existing objects. Object mappers will populate all fields annotated with @Setting
, using the type of a field to determine which type serializer to use. As of v3.7, parameterized object-mappable classes can be used, and their type parameters will be resolved in fields.
String
| float
| double
| int
| long
| byte
|
ConfigurationNode
| char
| Map
| List
| Set
| URL
|
@ConfigSerializable
objects | boolean
| UUID
| Pattern
| any array | URI
|
enum
class values | short
| Path
| File
Other supported types may be added by registering custom TypeSerializers, explained below.
Create a new child TypeSerializer
, which can be used when creating a node:
final TypeSerializerCollection own = TypeSerializerCollection.defaults().childBuilder()
.register(MyCustomType.class, MyCustomTypeSerializer.INSTANCE)
.build();
This can be passed through to a configuration loader as well. Here is an example with the YAML loader.
final YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
.defaultOptions(o -> o.serializers(own))
.path(Paths.get("config.yml"))
.build()
As of version 4.0, the object mapper has defined extension points, set up on an ObjectMapper.Factory.Builder
. See their class documentation for details on what each one does.
Here's a example of a standalone ObjectMapper setup. This uses a few value types, some nodes, and shows that type parameters are interpreted where specified.
public final class ObjectMapperExample {
private ObjectMapperExample() {}
public static void main(final String[] args) throws ConfigurateException {
final Path file = Paths.get(args[0]);
final HoconConfigurationLoader loader = HoconConfigurationLoader.builder()
.defaultOptions(opts -> opts.shouldCopyDefaults(true))
.path(file) // or setUrl(), or setFile(), or setSource/Sink
.build();
final CommentedConfigurationNode node = loader.load(); // Load from file
final MyConfiguration config = MyConfiguration.loadFrom(node); // Populate object
// Do whatever actions with the configuration, then...
config.itemName("Steve");
config.saveTo(node); // Update the backing node
loader.save(node); // Write to the original file
}
@ConfigSerializable
static class MyConfiguration {
private static final ObjectMapper<MyConfiguration> MAPPER;
static {
try {
MAPPER = ObjectMapper.factory().get(MyConfiguration.class); // We hold on to the instance of our ObjectMapper
} catch (final SerializationException e) {
throw new ExceptionInInitializerError(e);
}
}
public static MyConfiguration loadFrom(final ConfigurationNode node) throws SerializationException {
return MAPPER.load(node);
}
private @Nullable String itemName;
@Comment("Here is a comment to describe the purpose of this field")
private Pattern filter = Pattern.compile("cars?"); // Set defaults by initializing the field
// As long as custom classes are annotated with @ConfigSerializable, they can be nested as ordinary fields.
private List<Section> sections = new ArrayList<>();
// This won't be written to the file because it's marked as `transient`
private transient @MonotonicNonNull String decoratedName;
public @Nullable String itemName() {
return this.itemName;
}
public void itemName(final String itemName) {
this.itemName = requireNonNull(itemName, "itemName");
}
public Pattern filter() {
return this.filter;
}
public List<Section> sections() {
return this.sections;
}
public String decoratedItemName() {
if (this.decoratedName == null) {
this.decoratedName = "[" + this.itemName + "]";
}
return this.decoratedName;
}
public <N extends ScopedConfigurationNode<N>> void saveTo(final N node) throws SerializationException {
MAPPER.save(this, node);
}
}
@ConfigSerializable
static class Section {
private String name;
private UUID id;
// the ObjectMapper resolves settings based on fields -- these methods are provided as a convenience
public String name() {
return this.name;
}
public UUID id() {
return this.id;
}
}
}