Modifiers & Validation
The Codec interface provides dozens of default instance methods that allow you to modify how an existing codec behaves without needing to rewrite it. It also features a powerful schema validation system.
Basic Modifiers
These modifiers change the structural type of the codec, returning a newly wrapped codec instance.
Method | Description |
|---|
list()
| Transforms a Codec<T> into a Codec<List<T>>. It automatically handles iterating through serialized arrays. |
collection(Supplier)
| Similar to list(), but allows you to specify the exact collection type (e.g., HashSet::new). |
optional()
| Transforms a Codec<T> into a Codec<Optional<T>>. If the field is missing or empty, it safely yields Optional.empty(). |
nullable()
| Modifies the codec to safely handle null Java values. If it encounters a null during encoding, it writes an empty state. If it reads an empty state, it yields null. |
orElse(T fallback)
| Provides a default fallback value. If the decoding process fails, the codec catches the error, attaches it to the DataResult as a warning, and yields your fallback value. |
xmap(forward, backward)
| Transforms a Codec<T> into a Codec<R> by providing two-way conversion functions. Useful for wrapping primitive values into custom classes. |
flatXmap(forward, backward)
| Similar to xmap, but the conversion functions return a DataResult. Useful when the conversion process itself can fail and you need to provide a contextual error message. |
fieldOf(name)
| Binds this codec to a specific string key/name, returning a FieldBuilder definition for use inside the RecordBuilder. |
unchecked()
| Performs an unsafe cast of the codec to a different type. Use with extreme caution. |
Schema Validation
AbyssalLib's Codec API includes a robust schema validation engine. These modifiers do not change the type of the codec, but they attach strict rules that are checked during both encoding and decoding.
If validation fails, the codec will yield an error DataResult with detailed bounds information.
Method | Description |
|---|
range(min, max)
| Restricts a numerical codec (Int, Double, Float, Long) to an inclusive minimum and maximum. |
positive()
| Shorthand for requiring a numerical codec to yield a value strictly greater than 0. |
minLength(len)
| Enforces a minimum character length on String codecs, or a minimum element size on List and Map codecs. |
maxLength(len)
| Enforces a maximum length constraint. |
regex(pattern)
| Requires a String codec to strictly match a provided regular expression. |
oneOf(values...)
| Restricts a String or primitive codec to only accept values from the provided array. |
Example: Validated Field
Validation modifiers are typically chained directly onto the base codec before defining it as a field.
public static final Codec<WeaponConfig> CODEC = RecordBuilder.create(instance ->
instance.group(
// Must be exactly "sword", "axe", or "mace"
Codecs.STRING.oneOf("sword", "axe", "mace")
.fieldOf("weapon_type")
.forGetter(WeaponConfig::type),
// Must be between 1.0 and 100.0
Codecs.DOUBLE.range(1.0, 100.0)
.fieldOf("damage")
.forGetter(WeaponConfig::damage),
// Value must be greater than 0
Codecs.INT.positive()
.fieldOf("durability")
.forGetter(WeaponConfig::durability),
// Array must contain at least 1 enchantment, max 5
EnchantCodec.CODEC.list().minLength(1).maxLength(5)
.optionalFieldOf("enchants", List.of())
.forGetter(WeaponConfig::enchants)
).apply(instance, WeaponConfig::new)
);
Custom Validation
If the built-in validators do not meet your requirements, you can write custom validation logic using .validate(). This allows you to check state against external systems or write complex cross-variable logic.
// Creates a String codec that fails if the string is empty
Codec<String> NOT_EMPTY_STRING = Codecs.STRING.validate(
str -> !str.trim().isEmpty(),
str -> "String cannot be blank or only whitespace!"
);
05 June 2026