FasterXML / jackson-modules-base

Uber-project for foundational modules of Jackson that build directly on core components but nothing else; not including data format or datatype modules
Apache License 2.0
166 stars 77 forks source link

Generate reflection-free Jackson serializers at compile time #247

Open mariofusco opened 1 month ago

mariofusco commented 1 month ago

I'm opening this issue only to start a discussion about the possibility of automatically generating reflection-free Jackson (de)serializers. My current understanding is that Afterburner first and Blackbird now are 2 modules designed with exactly this purpose. Their drawback however is that they generate at runtime reflection-free accessors for a specific field/method while, thanks to Quarkus architecture and tools like Jandex and Gizmo, I could be able to generate at build time the serializer for a whole pojo like I started doing in this PoC.

I would like to continue that experiment, but before moving too much forward and wasting my time, I wanted to share my doubts about it:

  1. Is there anything similar that we could do directly in Jackson, instead of implementing it in a Quarkus extension? For instance have you considered using an annotation scanner at build time and triggering the generation of the serializers for the annotated user's pojos from a maven plugin?
  2. Even if this is not feasible (or simply out of scope for Jackson) is there any rework/refactor that we could implement in Jackson in order to avoid duplicating in the Quarkus extension all the complex Jackson serialization logic?
  3. My main concern regarding the former point is that Jackson serialization comes with a tons of features and edge cases and it will be extremely hard and error-prone to reimplement all of them in Quarkus. The strategy that I have in mind (admittedly suboptimal) to workaround this problem is having a list of the Jackson's features that we support and generate seralizers only against them and when we bump into an unknown annotation we avoid to generate the serializer at all for that specific pojo, thus falling back to the reflection based Jackson mechanism. Do you see any issue with this approach or have any better suggestion?

Any feedback is welcome. /cc @cowtowncoder

cowtowncoder commented 1 month ago

Quick note: yes, I have occasionally thought about such a thing, but no, nothing implemented.

Afterburner and Blackbird are sort of half of the puzzle: to remove runtime use of Reflection for constructing and setting/getting values. But to do generation itself on runtime. Nothing in Jackson stack works on build time, so this would need to be a whole new set of machinery. And even with that, the concern of complicated handling possible deviating from vanilla/default handling is quite real.

If someone was to consider build-time generation of matching (de)serializers -- or, maybe better yet, sort of adapters called generically (sort of minimalistic handler classes Afterburner/Blackbird generate), I'd be happy to help.

It could be something like generating bytecode for "setter"/"getter" classes associated with value types, for which general "BuildTime[De]Serializer"s could be built; containing something like "setString(String property/int index, String value)" (and similarly for "getXxx()"

Or more generally: yes, I'd be perfectly happy adding small extension points that might be needed for build-time generated handlers.

And directly on points:

(1) As per above, no, I don't think there's anything more than small pieces by AB/BB (2) Not sure, but I would be willing to help do refactor if some was identified to help (3) Yes, this makes sense to me. And sounds similar to how AB/BB downgrade to standard handling when necessary.