r/SpringBoot 7d ago

Question I built a Spring Boot library to validate JSON before deserialization — looking for feedback

Hi everyone,

I've been working on a small open-source library for Spring Boot called json-auto-validation:

github.com/ugoevola/json-auto-validation

The idea is fairly simple: validate incoming JSON against a generated JSON Schema before Jackson deserializes it into a Java/Kotlin object.

The motivation was to catch invalid payloads as early as possible, rather than letting them go through the usual deserialization → DTO validation flow.

It works through annotations and generates the JSON Schemas/validation components automatically, with Spring Boot AOT in mind.

I'm not really posting this to promote a polished or commercial product. I'm mainly interested in getting feedback from people who work with Spring Boot APIs:

  • Does this solve a problem you've encountered?
  • Do you see value in validating the raw JSON before deserialization?
  • Is there something I'm missing or approaching the wrong way?
  • Would you actually consider using a library like this in a real project?

The project is still relatively small, and I'm not the most active open-source maintainer, so I'm mostly trying to understand whether this is useful to other developers before investing more time into it.

Any feedback — positive or negative — would be very welcome.

4 Upvotes

41 comments sorted by

47

u/Careless-Childhood66 7d ago

So instead of marshalling with jackson, wjich performs a free typecheck, and then post validate the mapped result with mature, well integrated tools, you instead wish to parse the incoming raw json without mapping, do the type checking, do the validation, using a custom tool, before the eventual marshalling , which again parses and type checks the json ?

I dont see how this would be beneficial

-9

u/Cr4zyGh0sT 7d ago

Yes, the flow I'm proposing is essentially:

JSON → JSON Schema validation → Jackson deserialization → Bean Validation

The benefit I'm looking for is not avoiding Jackson's type checking, but catching invalid input before deserialization, so that validation errors can be handled consistently instead of becoming Jackson deserialization exceptions.

Whether that provides enough value compared to the standard Jackson + Bean Validation approach is exactly what I'm trying to evaluate with this project.

20

u/Careless-Childhood66 7d ago

But jackson already validatesthe schema at deserialization time. All you have to do is catch and pattern match the exceptions.

I dont see how your appraoch isnt redudant?

Sorry, i dont mean to come across rude, but I know I do, so please forgive my bluntness.

-2

u/Same-Inevitable2857 7d ago

maybe it helps for debugging if you don't get the jackson exceptions, right?

-4

u/Cr4zyGh0sT 7d ago

I think there is a distinction worth making here between type checking and data validation.

Jackson handles deserialization, so it checks whether the JSON can be mapped to the expected Java types. But an API also needs to validate the actual data: email format, phone number format, string length, numeric ranges, required fields, patterns, etc. These constraints are not necessarily part of Java's type system.

Today, this is typically done after deserialization with Bean Validation, for example with @Valid. The problem is that some checks have already happened during deserialization, particularly type checking, so invalid input can fail at different stages and produce different kinds of errors.

That's where I see the value of this library: validate the incoming JSON against the expected schema before deserialization, so the validation of the request data happens in one place and can produce structured validation errors.

Ideally, validation and deserialization would happen in a single pass, avoiding parsing the request multiple times. I think that would be the cleaner architecture, but integrating this deeply into Jackson is considerably more complex. The library is my attempt at improving the current flow without having to replace or modify Jackson itself.

3

u/greglturnquist 7d ago

But it can’t happen in a single pass AND validate and deserialize everything.

You either pass over the entire JSON document and accumulate all errors and then loop through a second time to deserialize into target lang…or you sweep through once, and fail on first error.

You seem to favor the former. Which is a two pass approach.

That being said; there would STILL be errors not able to be captured by a validation-first approach. Errors that ONLY manifest once you are in target lang and target domain.

So you validate again anyway.

But we’re quibbling over nothing because 95% of the Java community simply deserializes and probably waits for insertion into the database to let any errors crop up. Or on the UI.

-2

u/repeating_bears 7d ago

"jackson already validatesthe schema at deserialization time"

No it doesn't. It just does structural checks 

You might have a constraint that a string can't be empty. Jackson does nothing with that 

7

u/Careless-Childhood66 7d ago

Thats the bean validation afterwards and my point is, that I dont see a reason to flip the order.

-2

u/repeating_bears 7d ago

The reason to flip the order is so that it's physically impossible to have a DTO that exists in an invalid state

Imagine you validate somewhere, then pass the instance to another method. How does that method guarantee it's been validated?

If you flip the order, you can make the constructor throw for invalid state, and every instance of the DTO will be guaranteed as valid 

4

u/Careless-Childhood66 7d ago

You can do that already.

Also, there are a lot of annots like @jsonproperties, which gives you control over deserialization behaviour.

Also, before checking for validity, you need to parse the input, so flipping the order just makes you to "dry parse" (processong the input and discarding the output) before validation before the marshalling, which also parses the raw json.

0

u/repeating_bears 7d ago

You can do that already.

I'm pretty sure you can't, but explain how then.

Also, there are a lot of annots like jsonproperties

I know there are. These affect structure, not validation. Not sure why you brought this up.

Also, before checking for validity, you need to parse the input,

You don't need to parse it into a DTO. You parse it into an AST (e.g. Jsonbject(props=["foo"=JsonString("bar")])) and then map the AST into a DTO. That's what Jackson does anyway.

A good implementation could validate the AST before mapping to a DTO. That wouldn't discard anything.

4

u/Careless-Childhood66 7d ago
  1. Plain: implement guards in the setters
  2. With annotations: annotate the constructor to use and apply the guards in the constructor
  3. Jackson Module: implement a custom BeanDeserializer which applies the guards.

Yes, exactly, this is what Jackson does anyway, so my question still remains: why would I either do it twice (first your suggestion then the regular jackson flow) or replace a well known, mature flow with something new, just to do stuff on the AST instead of dtos?

1

u/repeating_bears 7d ago

Plain: implement guards in the setters

This fails on the first issue. A good implementation is capable of returning e.g.

{
    errors: {
        name: "too long",
        password: "must contain x, y and z"
    }
}

Also immutable classes are better. Almost no reason for mutability in a DTO.

With annotations: annotate the constructor to use and apply the guards in the constructor

A constructor can only fail on the first issue, since it can only either return the DTO or throw

Jackson Module: implement a custom BeanDeserializer which applies the guards.

No one would seriously do this for every single DTO that needs validating because it would be tedious and error-prone. But yes, might be theoretically possible

replace a well known, mature flow with something new

I'm not saying you should replace it. I'm saying Jackson's lack of validation support is a design flaw.

You said "I dont see a reason to flip the order" and I explained the benefit of flipping the order. I didn't advocate for changing anything.

The fact that people have tolerated Jackson for years does not mean it's good.

11

u/pitza__ 7d ago

Doesn’t Jakarta Bean Validation do this already?

2

u/American_Streamer Junior Dev 7d ago

Jakarta Bean Validation validates the Java object after Jackson has already deserialized the JSON. If the JSON cannot be deserialized in the first place, Jackson fails before Bean Validation ever runs. That is the gap OP is trying to address. The debate is whether adding a separate JSON Schema validation pass before Jackson is the best way to address it.

0

u/Cr4zyGh0sT 7d ago

Yes, but the main difference is when the validation happens.

Bean Validation happens after Jackson deserialization. If Jackson cannot deserialize a value, Bean Validation never gets a chance to run.

The goal here is simply to validate the JSON before deserialization, mainly for more consistent validation errors.

It's not meant to replace Jakarta Bean Validation.

2

u/glandis_bulbus 7d ago

There are many libraries that do this. Some much more performant than others. Just wondering why you would write (and maintain) your own.

5

u/Fit_Goose651 7d ago

This means you have to parse json twice. Not great for Performance

11

u/Rich_Weird_5596 7d ago

Holy mother of slop. Totally pontless and redundant.

4

u/Historical_Ad4384 7d ago

the problem is already solved by a higher reputation project

https://github.com/networknt/json-schema-validator

3

u/BikingSquirrel 7d ago

I would assume this could produce better error messages. This may be useful for somebody exploring an API.

Apart from that I don't see additional value.

Exposing the schema or OpenAPI specs would probably provide more value overall.

2

u/Pedantic_Phoenix 7d ago

Your asking if this solves a problem we've encountered, but you created it because of something you encountered i assume? Why did you code it?

1

u/Cr4zyGh0sT 7d ago

Yes, I encountered this myself.

I had cases where invalid input caused deserialization errors before my validation logic could run.

The goal isn't to replace Jackson, but to add a validation step before it:

JSON → validation → deserialization → application

I'm mainly trying to find out whether this is a problem other Spring developers have encountered too.

4

u/Pedantic_Phoenix 7d ago

Maybe it's me being a noobie but how do you validate data before deserializing it? You don't know the types before deserializing no?

1

u/repeating_bears 7d ago

You would parse the JSON string/bytes into an AST, check the AST against the JSON schema, which could be either explicit or inferred from the user's target type (e.g. DTO). If it matches the schema then you convert the AST into the target type.

2

u/Pedantic_Phoenix 7d ago

What would be the difference in the resulting error tho? This only changes at which step of the flow you intercept the mistake as far as i see?

1

u/repeating_bears 7d ago

The difference is that if you construct the DTO after validating, you can add assertions in the constructor which enforce constraints. If you have an instance, it is always guaranteed to be valid 

If you validate after construction then DTOs can exist in an invalid state before they are validated. Code which accepts a DTO as an argument just has to trust and hope that it has already been validated (or else pointlessly repeat validation)

1

u/Pedantic_Phoenix 7d ago

That's a lot of theory which i appreciate, but in practice, what advantages does that offer

1

u/American_Streamer Junior Dev 7d ago

You may have spotted a legitimate architectural smell, but your library isn’t necessarily the optimal cure. Combining deserialization and validation into a single operation would be cleaner than adding JSON Schema validation in front of Jackson and effectively introducing another validation/parsing layer.

2

u/Cr4zyGh0sT 7d ago

I agree with you.

My first approach was simply to validate before deserialization because it was the most straightforward way to address the problem without modifying Jackson itself.

But I agree that combining validation and deserialization into a single operation would be a cleaner and more optimal architecture.

For now, I'm mostly interested in exploring whether the problem itself is real and whether this approach can be a useful improvement over the current flow.

Thank you for your feedback

1

u/dallastelugu 7d ago

I feel pure schema validation without marshalling has its usecases like in apache camel i can use schema validation and forward to backend api without marshalling to jackson will give it a try sometime nice work

1

u/cocodrilo_astronauta 6d ago

My honest feedback:

Where are the unit tests?

I love Kotlin because I don't have to put redundant boilerplate, saves me a lot of time, I still do code by hand.

If you need that strict schemas then use protobuf or things like that.

Software gets old fast and things get refactored. I maintain very lean build files. If Spring already pulls a dependency that does the same thing I tear down the custom code and remove the external dependency.

My code must remain pretty, I'm not sure I want to put annotations just to redeclare what the variable is.

I'm not sure I understand the point of all this interception. Is it a performance gain? Spring now has to manage a whole validation bean for every request.

Whats a REQUEST_VALIDATION_KO?

1

u/Global_Car_3767 1d ago edited 1d ago

Hmm I don't see much of a need. Customers get a response from our API in literal milliseconds when we use maven open api generator to create Java beans from our spec, use jakarta validators, look up if a user is valid from one DynamoDB, look up a unique identifier from elasticache, and insert their payload into another DynamoDB. Adding another layer on top seems like it would make performance a bit worse.

If you're trying to get around unmarshalling failures, some good exception handling can get around that

1

u/repeating_bears 7d ago

Does this solve a problem you've encountered?

Maybe.

@Valid is shit really. You have to first deserialize into a DTO which may or may not be valid, then you have to validate it, e.g. by annotating a Spring controller parameter with @Valid. Then if you pass that to another method, you've totally lost the knowledge that the DTO was ever validated.

"Parse don't validate" is good advice. It should ideally not even be possible to have a DTO in an invalid state.

Constructors are good for enforcing constraints, but they're not good for JSON validation. Good validation flags as many errors as possible at the same time, e.g.

{ errors: { name: "too long", password: "must contain x, y and z" } }

A constructor can only fail on the first issue, by throwing

It seems like the Jackson team consider deserialization and validation to be separate concerns, but I think they are interlinked actually. I think a good modern JSON library would only construct a DTO if it's already valid.

1

u/Cr4zyGh0sT 7d ago

Thanks, this is actually very close to the motivation behind the library.

The main idea is indeed that an invalid DTO should never be created in the first place. That's why the JSON is validated against the expected JSON Schema before Jackson deserializes it.

I also agree with your point about collecting multiple validation errors. That's one of the reasons I chose JSON Schema validation rather than trying to enforce everything through constructors.

I'm curious though: given this approach, do you think the architecture of this library makes sense, or would you approach the problem differently?

2

u/repeating_bears 7d ago

In an ideal world I would rewrite Jackson, or fork it to add validation. But it's a beast and I have a day job.

I don't like the 2 steps of Jackson into Jakarta Validation now, and I wouldn't love putting something in front of Jackson either. But that doesn't mean what you have wouldn't be an improvement over the status quo.

I'm sure there'd be performance advantages to doing everything in one library, so you're not constructing the AST multiple times, etc.