-
Notifications
You must be signed in to change notification settings - Fork 0
Entity Binding
Binding is a derived view over the canonical tree. You read the tree into a POJO when you want types, and merge a POJO back into the tree when you want to write — but the tree stays the source of truth.
DbConfig db = cfg.loadAs(DbConfig.class, codec); // bind the whole tree + run @PostInjectloadAs binds the whole tree to a fresh instance and runs @PostInject. For control over
options, get an EntityBinder:
EntityBinder<DbConfig> binder = cfg.bind(DbConfig.class, codec, options);
DbConfig db = binder.bind();
List<LoadIssue> issues = binder.lastLoadIssues();db.maxPool = 25;
cfg.mergeFrom(db, codec); // merge the POJO INTO the tree, then cfg.save()A binding save merges, it never replaces:
- The tree wins on conflict / unknown keys survive. A key a user added by hand that the POJO doesn't declare stays in the file.
- The POJO value wins for declared keys. Your in-memory change is written.
- Comments declared via @Comment are seeded during the merge.
cfg.setValue("legacy.extra", "kept");
cfg.mergeFrom(new DbConfig(), codec);
cfg.contains("legacy.extra"); // still true — unknown keys survive the mergeBindOptions controls how a value that can't be coerced to its field type is handled.
import br.com.finalcraft.everyconfig.binding.BindOptions;
// LENIENT (default): a bad value is recorded as a LoadIssue and the field keeps its real default
EntityBinder<Cfg> b = cfg.bind(Cfg.class, codec);
Cfg c = b.bind();
b.
lastLoadIssues(); // e.g. ['port' = 'NaN' (expected int): ...]
// STRICT: the first mismatch throws BindException
cfg.
bind(Cfg .class, codec, BindOptions.defaults().
withCoercion(BindOptions.Coercion.STRICT)).
bind();A LoadIssue carries the key, the rawValue, the targetType and a message. A @PostInject method may
take a List<LoadIssue> to inspect them and decide whether to reject the config.
Lenient keeps the real default. The bad node is removed from a working copy of the tree and the bind is retried, so the field keeps the POJO's actual default value — not a zeroed placeholder.
BindOptions.ObsoletePolicy decides what happens to a file key the bound schema no longer declares:
-
PRESERVE(default) — keep it untouched (the tree always wins). -
REMOVE— strip it from the tree during the merge (opt-in; destroys data the schema dropped).
cfg.bind(Cfg.class, codec, BindOptions.defaults().withObsoletePolicy(BindOptions.ObsoletePolicy.REMOVE));Because the mapper is plain Jackson, native annotations work alongside EveryConfig's:
@JsonProperty, @JsonIgnore, @JsonAlias, @JsonTypeInfo/@JsonSubTypes (polymorphism), etc.
class Holder {
@JsonProperty("max-pool") int maxPool = 10;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({@JsonSubTypes.Type(value = Circle.class, name = "circle")})
Shape shape;
}Polymorphic round-trips work and the type tag survives a merge.
→ See also Annotations · @Id Collections
EveryConfig · br.com.finalcraft:EveryConfig · One config API, every format, comments included · made by Petrus Pradella
Getting Started
Core Concepts
Typed Binding
Operations
Reference
Contributing