This annotation processor lets you define mappers and required keys on key-value structures with known String
keys.
Example
@Data(String.class) // type of the values of your data source
abstract class User {
@Key("name")
abstract String name();
@Key("age")
abstract int age();
}
This will generate code similar to the following:
@Generated
class User_Parser {
static User parse(Function<String, String> f) {
return parse(f, key -> new IllegalArgumentException("Missing required key: <" + key + ">"));
}
static User parse(Function<String, String> f, Function<String, RuntimeException> errMissing) {
return new UserImpl(
Optional.ofNullable(f.apply("name")).map(nameMapper).orElseThrow(() ->
errMissing.apply("name")),
Optional.ofNullable(f.apply("age")).map(numberMapper).orElseThrow(() ->
errMissing.apply("age")));
}
private static class UserImpl extends User {
String name;
int age;
UserImpl(String name, int age) {
this.name = name;
this.age = age;
}
String name() { return name; }
int age() { return age; }
}
}
which can be used like this:
Map<String, String> m = Map.of("name", "Heiko", "age", "26");
User user = User_Parser.create()
.nameMapper(Function.identity())
.ageMapper(Integer::parseInt) // throw runtime exception to signal parse error
.prepare() // for when the abstract class has a constructor and fields
.parse(m::get);
assertEquals("Heiko", user.name());
assertEquals(26, user.age());
Skew rules
Whether a key is considered required or optional is determined by its type K
, using these rules:
Key type | Skew |
---|---|
K (exact match) |
required |
Optional<K> |
optional |
Optional{Int|Long|Double} |
optional |
Running tests
./gradlew core:clean core:test examples:clean examples:test