Built-in converters
Ravel provides converters (priority 1) for these types, automatically applied during config.getValue() and config.getOptionalValue():
| Type | Behavior |
|---|---|
|
Pass-through (no conversion) |
|
Truthy values: |
|
Via |
|
Via |
|
Single-character string; longer values throw |
|
Via |
|
Via |
|
Via |
|
Parses the numeric value and wraps it ( |
|
ISO-8601 format: |
|
ISO-8601 format: |
|
ISO format: |
|
ISO format: |
|
ISO format: |
|
ISO format: |
|
ISO format: |
|
ISO format: |
|
ISO format: |
|
Via |
Enum types are converted too (matched by enum constant name), but through the implicit-converter mechanism described below, not by a registered built-in. Collection types have no converter: List<T> values come from the getValues() / getOptionalValues() methods (comma split, built on the array conversion path), and List<T> / Set<T> injection is handled by the CDI layer.
Arrays
// microprofile-config.properties
app.items=foo,bar,baz
app.ids=1,2,3,4
// Code
String[] items = config.getValue("app.items", String[].class);
// Result: ["foo", "bar", "baz"]
Integer[] ids = config.getValue("app.ids", Integer[].class);
// Result: [1, 2, 3, 4]
Collections
getValue() does not accept collection classes — config.getValue("app.items", List.class) throws, because no converter exists for List. Multi-valued properties are read through the dedicated methods:
List<String> items = config.getValues("app.items", String.class);
Optional<List<Integer>> ids = config.getOptionalValues("app.ids", Integer.class);
In CDI beans, List<T> and Set<T> can be injected directly with @ConfigProperty (comma split plus element-wise conversion).
Implicit converters
If no registered converter matches a type, Ravel falls back to an implicit converter (MicroProfile Config §5.2):
-
Public constructor taking
String:new MyType(string) -
Static method
valueOf(String):MyType.valueOf(string) -
Static method
parse(CharSequence):MyType.parse(charseq)
public class MyDuration {
private final long millis;
// This public constructor enables implicit conversion
public MyDuration(String iso8601) {
this.millis = Duration.parse("PT" + iso8601).toMillis();
}
}
// microprofile-config.properties
app.timeout=1H30M
// Code
MyDuration timeout = config.getValue("app.timeout", MyDuration.class);
// Internally calls: new MyDuration("1H30M")
Custom converters
Register a custom Converter<T> for types not covered by built-in or implicit converters:
import org.eclipse.microprofile.config.spi.Converter;
public class ColorConverter implements Converter<Color> {
@Override
public Color convert(String value) {
if (value == null || value.isEmpty()) {
throw new IllegalArgumentException("Color cannot be empty");
}
if (value.startsWith("#")) {
// Hex format: #RRGGBB
return Color.decode(value);
} else if (value.equalsIgnoreCase("red")) {
return Color.RED;
} else if (value.equalsIgnoreCase("green")) {
return Color.GREEN;
} else if (value.equalsIgnoreCase("blue")) {
return Color.BLUE;
}
throw new IllegalArgumentException("Unknown color: " + value);
}
}
Converter priority
Registered converters compete by priority — the highest priority wins, and on ties the last registered converter overrides the previous one:
-
Built-in converters — priority 1
-
Converters discovered via ServiceLoader — their
@jakarta.annotation.Priorityvalue, or 100 when the annotation is absent (the standard application priority) -
Converters from
ConfigBuilder.withConverter(type, priority, converter)— the explicit priority argument (no special rank: 100 is the conventional value here too)
When no registered converter exists for the requested type, Ravel derives one: arrays are converted component-wise, and other types go through the implicit-converter fallback. If nothing applies, IllegalArgumentException is thrown.
Example: JSON converter
public class JsonConverter<T> implements Converter<T> {
private final ObjectMapper mapper;
private final Class<T> targetType;
public JsonConverter(Class<T> type) {
this.targetType = type;
this.mapper = new ObjectMapper(); // or your serialization library
}
@Override
public T convert(String value) {
try {
return mapper.readValue(value, targetType);
} catch (Exception e) {
throw new IllegalArgumentException("Invalid JSON for " + targetType.getName(), e);
}
}
}
// Usage
MyConfigClass config = ConfigProvider.getConfig()
.getValue("app.config.json", MyConfigClass.class);
Next
-
Property Expressions — reference other configuration values
-
CDI Integration — inject converted values via
@ConfigProperty