I am doing quite similar stuff at my current day job 👍🏻
Hope My suggestions can help. 1# “NO” to using a custom de/serializer. I started using @JsonTypeInfo at work to avoid exactly that. Having to write custom de/serializers for every DTO class. 2# Use Id.Name instead. I suggest to change DEDUCTION based handling on REST layer to JsonTypeInfo.Id.NAME <http://jsontypeinfo.id.name/> based. This will require change in REST API specification, but in the long term it will be easier to debug/maintain. Imagine how much a troublesome it would be for a server to having to “assume” every time what the client intends to do with the request! 3# Maintain `@JsonSubTypes.Type.name` attribute as constants. Simple coding convention, maintain a variable in one place. Since @JsonTypeInfo provides polymorphic type handling which is already complex, hard-coding type id will get back to you soon. It took me only a few days … :( Take below code for example # AS-IS ```java @JsonSubTypes({ @JsonSubTypes.Type(value = AndDbDto.class, name = AND_VALUE), @JsonSubTypes.Type(value = OrDbDto.class, name = OR_VALUE) }) ``` # TO-BE ```java @JsonSubTypes({ @JsonSubTypes.Type(value = AndDbDto.class, name = AND_VALUE), @JsonSubTypes.Type(value = OrDbDto.class, name = OR_VALUE), @JsonSubTypes.Type(value = ConditionDbDto.class, name = CONDITION_VALUE) }) public enum DbExpressions { AND, OR, CONITION // Constants public static final String AND_VALUE = “and”; public static final String OR_VALUE = “or”; public static final String CONDITION_VALUE = “condition”; } ``` On Thursday, May 4, 2023 at 12:51:30 PM UTC+9 Tatu Saloranta wrote: > On Mon, May 1, 2023 at 4:11 PM Marcus Biel <indu...@gmail.com> wrote: > > > > Hello everyone, > > > > I'm dealing with a project that has a complex setup, and I'm trying to > simplify it. Our application allows users to establish Conditions or > Filters in the UI. These Conditions/Filters are stored in a database and > can be used to limit database queries using WHERE conditions. > Interestingly, these filters can be built recursively from other filters. > > While I haven't built anything quite like this myself, I have seen > similar use cases, for what it is worth. > So maybe others with direct experience can share their learnings. > > > > > Our current structure involves DTO classes marked with Jackson > annotations, which build a hierarchy. This hierarchy is mapped into > business objects, despite not having real business logic. These are later > converted into database DTOs. The system is quite intricate as it spans > across three layers - REST, Domain, and ORM Mapper. Additionally, the code, > written two years ago by another developer, extensively uses Jackson > Annotations, which increases the complexity and makes it difficult for me > to understand. > > Yes, that sounds like a complex beast. > > > Here are the two code snippets for the REST DTOs and the ORM Mapper: > > > > REST DTO sample: > > > > @JsonTypeInfo(use = DEDUCTION) > > @JsonSubTypes({ > > @Type(AndDto.class), > > @Type(OrDto.class), > > @Type(ConditionDto.class), > > @Type(FieldDto.class), > > @Type(LabelDto.class) > > }) > > public interface ExpressionDto { > > } > > > > ORM layer DTO sample of basically the same interface: > > @JsonTypeInfo( > > use = JsonTypeInfo.Id.NAME, > > property = "type") > > @JsonSubTypes({ > > @JsonSubTypes.Type(value = AndDbDto.class, name = "and"), > > @JsonSubTypes.Type(value = OrDbDto.class, name = "or"), > > @JsonSubTypes.Type(value = ConditionDbDto.class, name = "condition"), > > @JsonSubTypes.Type(value = FieldDbDto.class, name = "field"), > > @JsonSubTypes.Type(value = LabelDBDto.class, name = "label") > > }) > > public interface ExpressionDbData { > > } > > > > --- > > I'm faced with the task of integrating about 15 more logical operators > into the model, on top of the two already present - Equals for Field and > Contains for Labels. Incorporating these operators would significantly ramp > up the complexity of the existing model. > > > > Each field type is linked to a data type - Text, Number, or Date. Each > data type should support its specific logical comparison operators: > > TEXT: EQUAL, EQUAL_IGNORE_CASE, NOT_EQUAL, START_WITH, ENDS_WITH, > CONTAINS > > NUMBER: EQUAL, NOT_EQUAL, GREATER, SMALLER, GREAT_EQUAL, SMALLER_EQUAL > > DATE: EQUAL, BEFORE, AFTER, BEFORE_OR_EQUAL, EQUAL_OR_AFTER > > > > I'm searching for a more straightforward yet valid method to accept > these conditions/filters from the frontend, store them in the database, and > later apply them to database queries. At present, we're achieving this with > a custom JPA CriteriaBuilder. > > > > Could using a custom Serializer / Deserializer or maybe an > AttributeConverter be helpful in this case? I'm not sure, and I'm open to > suggestions. Currently, the project is using the latest versions of Java > and Frameworks - Java 20, Hibernate 6.2, and Quarkus 3.x. I could leverage > Sealed classes if Jackson supports them. > > Custom deserializers can be tricky: obviously you can do anything and > everything, but at the same time if they need to interoperate with > standard Jackson (de)serializers there's quite a bit more work to do. > > Another approach that can be helpful is 2-pass (or multi-pass), in > which a JsonNode-based model is bound from/to JSON, modified, then > used as source for ObjectMapper. This can be used for all kinds of > pre-/post-processing. > > > > > Also, could someone clarify the difference between the use of DEDUCTION > in the REST layer and JsonTypeInfo.Id.NAME in the database layer version > of the same class? > > Ok, so: DEDUCTION basically means that no explicit Type Id (property, > or wrapper Object/Array) is used; type is deduced solely from the > existence of specific property names. Value types then must have, > each, at least one unique property name (something no other type has). > On serialization no type id is written, only regular properties. > > The other choice used is to add (and use) specific Object Property > (here "type") to contain Type Id; and as id use logical, annotated > "name" (instead of Java class, the main alternative). > Type Id is written as Object metadata in addition to regular > properties (or in general, regular value representation). > > I must say I am not sure why DEDUCTION was used here: I think it's > more common to use explicit Type Ids for both. > > -+ Tatu +- > > > > > Thanks a lot in advance! > > > > Marcus > > > > -- > > 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/14120539-f3f0-4458-b206-c2b31002a9e2n%40googlegroups.com > . > -- 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+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/jackson-user/002717d4-eb31-4ac1-90cc-b03f42fdddffn%40googlegroups.com.