Questions about how to integrate Bean Validation with Jackson

1,050 views
Skip to first unread message

João Cabrita

unread,
Jul 5, 2022, 2:34:00 PM7/5/22
to jackson-user
Hi,

I've been working on a module to integrate Bean Validation with Jackson: https://github.com/kewne/jackson-validation

The basic ideas are:
1. Without integrating validation into `@JsonCreator` calls, you can't then enforce invariants in class instances, e.g. this.name = Objects.requireNonNull(name);
with validation support, you can annotate params with @NotNull.
2. Bean Validation inspects the class and knows nothing about Jackson, so it generates paths using the Java definition;
with this module , you would be able to declare @JsonProperty("abc") String name and get violation paths with the correct names (instead of "name").

My approach so far has been to create my own value instantiator and perform validations before and after instantiation, but this fails because, if an object deep in the tree fails its validation, I don't know how to catch it so it shows up properly in violations tree:

class Root {
  private Nested nested;
}

class Nested {
  @NotNull private String name;
}

ObjectMapper.readValue("""
{
  "nested": {}
}
""", Root.class); //throws validation exception with Nested as root...

Any hints on how I might handle the nesting?

Thanks!

Tatu Saloranta

unread,
Jul 25, 2022, 5:15:00 PM7/25/22
to jackso...@googlegroups.com
It seems to me that the options really come down to 2 approaches for
combining Jackson and Bean Validation API:

1. Run them separately: Jackson binds data first, Validator then
validates the result. Less integrated so error messages do not tie
back to input offsets.
2. Implement custom version of Bean Validation API Validator that
somehow integrates into Jackson's handling

So far I have assumed that (1) is a reasonable path as although in
theory (2) could provide more meaningful information, the effort to
make that actually work well seems very high.
At least for the general case. Perhaps it would be more practical if
it's ok to modify POJOs to participate in the validation process.

As to nesting, the problem really is that your POJOs will not get any
chance to participate in traversal, or have access to context.

So unfortunately I can't think of much to help you here. But maybe others do.

Good luck!

-+ Tatu +-

Mantas Gridinas

unread,
Jul 25, 2022, 5:39:01 PM7/25/22
to jackso...@googlegroups.com
There's path traversal API (see https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-validator-factory-traversable-resolver). Perhaps that could be of use to you?

You might also want to look into the constraint violation api. As far as I remember (atleast in hibernate's implementation) you can get a reference to the failing value. Double check if you can get a reference to the field instance so you could reflect the jackson annotations.


--
You received this message because you are subscribed to the Google Groups "jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jackson-user/CAL4a10jCNsUNaWAw3n8skGdQM%2Bd%2BarfsmeEyPqEuaMswY79KEg%40mail.gmail.com.

João Cabrita

unread,
Jul 26, 2022, 10:04:09 AM7/26/22
to jackson-user
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!
Reply all
Reply to author
Forward
0 new messages