First of all, thank you for your answers.
> Run them separately: Jackson binds data first, Validator then
validates the result. Less integrated so error messages do not tie
back to input offsets.
This definitely works because Spring MVC does exactly that;
however, it has two flaws that I wanted to address:
1. when validation runs, I think the framework has "forgotten" where the original input came from, so you can't even tell if it came from JSON at all;
in practice, I can probably work around this by inspecting the request's content type;
2. (the most important IMO) is that it forbids you from enforcing class invariants strictly; for example:
record Person(@NotNull String name) {
Person {
Objects.requireNonNull(name);
}
}
The issue here is that, if the constructor arguments are not validated, a NullPointerException is thrown which will prevent the object graph from being deserialized...
and the only solutions I see at that point would be to either drop Objects.requireNonNull (i.e. stop enforcing the invariant) or use a delegate that does not enforce them (which means more boilerplate).
> There's path traversal API
I don't see how I could make use of this: I would be doing validation on deserialization into POJOs, so there's no reason not to access all of the nodes...
Thanks for your help, I'll try a couple of things and report back if I get anything working!