Jackson not invoking Custom deserializer when set on a field as well as for custom annotations #188
Replies: 10 comments 17 replies
-
Unfortunately usage via 3rd party frameworks (such as Spring, WebFlux, etc..) is not the best shape to treate issues in Jackson, since they have their own configuration settings, mechanisms and overrides --which happen to be the cause in most cases. They usaully wrap Jackson with something their own. So could you provide tests, classes, configurations in plain Jackson? That would be
Thanks! |
Beta Was this translation helpful? Give feedback.
-
Ok this is not going to work the way you envision it: incoming structure must match to some degree wrt outer |
Beta Was this translation helpful? Give feedback.
-
I am trying to parse the JSON structures of the ActivityStreams vocabulary into a hierachy of sealed interfaces (with the leaf nodes being records). The plan is to make something that parses ActivityPub responses into Java objects that can be handled with Java 21 pattern matching (with the hope that this will make the logic handling the responses as clear as possible). We'll see if I get there how that works out. I'm working my way through the examples of the ActivityStreams vocabulary and I'm currently stuck at example 13. Some (many, in fact) fields in the ActivityStreams JSON may be either the object inlined, or they may be a link to where the JSON for the object can be downloaded from. E.g. a property may be either this
or it may be a link to the object
So far, so good. I'm able to parse this. But then I ran into the fact that Link is often represented as just the URL as a string, i.e. the above will be just:
The first thing I tried was custom deserializers. But But then I thought I had found a solution by using I.e. I could make a converter like this
and use it like this (in a sealed inheritance hierarchy leaf node):
This worked fine for translating strings into Link objects, so I thought I had solved it... until I ran into an "actor" field that wasn't a string but an object. Then I got:
I.e. now only string is accepted. So now I'm wondering what to do...? |
Beta Was this translation helpful? Give feedback.
-
>>>> "Kim, Joo Hyuk" ***@***.***>:
Minimal reproduction would be great, thanks!
Before I start the work on creating a testcase: what I have done so far looks correct?
Ie. I haven't done anything that looks stupid or wrong in the definition
of the deserializer? (the class used in the constructor? The choice of
base class?)
And the placement and usage of the annotation looks correct?
This is so we can add to (most probably) jackson-databind repo.
Hopefully minimal and Jackson only so that we can just copy paste into [databind test suite.](https://github.com/FasterXML/jackson-databind).
Also, wrt interfaces, I don't think we need all of them especially reproduction input JSON is just one?
No, there is 3 actually.
The top level object is a Transaction of type Add (Transaction is a
subtype IntransitiveAction, which is a subtype of ActivityStreamsObject,
which in turn is a subtype of LinkOrObject).
Then there is a Person, that is a subtype of type Actor, that is a
subtype of ActivityStreamsObject which is a subtype of LinkOrObject.
And finally there is the object which is a string that should be turned
into a Link.
And the object is the one that turned into a problem when parsing
example 13, because there, instead of a string that should be turned
into a Link, there is an inlined object of type Image (which is a
subtype of Document which is a subtype of ActivityStreamsObject which is
a subtype the top level interface LinkOrObject).
https://www.w3.org/TR/activitystreams-vocabulary/#dfn-add
(I kinda wonder if the people writing the spec actually tried parsing
this stuff...? One thing I have flipped around is the inheritance order
of Action and IntransitiveAction because there they operated with
removing members in the derived class, and that... Java can't do... oh well!)
|
Beta Was this translation helpful? Give feedback.
-
At this point, the minimal reproduction really should focus on that one Record field with annotated custom deserializer; deserializer itself returning whatever constants test can verify and not try to do anything else. Failing test would go under So, assuming a simple reproduction is doable, we'd need a pr on |
Beta Was this translation helpful? Give feedback.
-
>>>> Tatu Saloranta ***@***.***>:
Ok; only 1 test fails (with 2.18.1-SNAPSHOT; build from `2.18` branch), and that for missing type id.
It does not indicate specific problem on missing deserializer.
No, but that error message comes even when this annotation isn't present
```
@JsonDeserialize(using = LinkDeserializerHandlingStringUrl.class)
```
The error message comes because a string is encountered
https://github.com/steinarb/record-field-deserializer-repro/blob/master/src/test/resources/json/activitystreams-vocabulary/example_012.json#L9
where a LinkOrObject is expected
https://github.com/steinarb/record-field-deserializer-repro/blob/master/src/main/java/no/priv/bang/ratatoskr/asvocabulary/LinkOrObject.java#L43
I managed to make this object field parse by annotating the field with
```
@JsonDeserialize(converter = StringToLinkConverter.class)
```
But then an object field that contained a LinkOrObject subtype failed:
https://github.com/steinarb/record-field-deserializer-repro/blob/master/src/test/resources/json/activitystreams-vocabulary/example_013.json#L9
(the test for this, you can currently see, is green)
Apparently ***@***.***(converter = StringToLinkConverter.class)`
made the field be "string" only, when what I hoped for was "parse
LinkOrObject normally, but if this is a string, create a Link object and
use the string value as Link.href".
But this is way too big for me to digest; I do not really have time to spend to fully understanding everything here.
What is needed is specific, targeted, minimal test case.
But FWTW: one thing that seems wrong in `LinkOrObject` definition is use of
include=JsonTypeInfo.As.EXTERNAL_PROPERTY
in class definition: it should only be used on properties, not classes.
I am not sure why you use that choice: either `As.PROPERTY` or `As.EXISTING_PROPERTY` would make more sense.
What was trying for here, was to use the LinkOrObject.type field to
determine the actual type to instantiate.
I used the example I found here and adapted to my type hierarchy
https://www.baeldung.com/java-jackson-polymorphic-deserialization#polymorphic-deserialization-using-jsontypeinfo-with-jsonsubtypes
|
Beta Was this translation helpful? Give feedback.
-
>>>> Tatu Saloranta ***@***.***>:
Another note: while `LinkDeserializerHandlingStringUrl` may return `null` for testing, it actually MUST also consume content: so it should call
p.skipChildren();
to make sure all content matching expected value is consumed.
This does not matter if the current content is a single scalar value; but if value was Object or Array (i.e. multi-token value), leaving content unread would lead to `JsonParser` state being incorrect and further parsing failing.
I don't think it matters here (adding that won't make test pass) but thought worth mentioning.
Ok, LinkDeserializerHandlingStringUrl.deserialize() now calls JsonParser.skipChildren().
|
Beta Was this translation helpful? Give feedback.
-
>>>> Tatu Saloranta ***@***.***>:
Hmmh. I must have bit different definition of minimized -- I was hoping for a single class, stand-alone unit test. :)
I don't have an easy way to get there, but the minimized-example branch
has cut the inheritance hierarchy down to the code that actually
participates in the test
https://github.com/steinarb/record-field-deserializer-repro/tree/minimized-example
But maybe I should have kept example_013 and the code needed for that,
since that is where ***@***.***(converter = StringToLinkConverter.class)`
broke...?
|
Beta Was this translation helpful? Give feedback.
-
Okay, can we pause the discussion here @steinarb ? and think of better way? I filed PR including test verifying that your statement (below) is not valid.
This leaves us to sealed class and interface hirearchy that you declared @steinarb, which you can personally isolate failing case further into smaller piece. We don't even need that many to start with. So please investigate further on your side (try googling) and comeback at different discussion. Thank you! |
Beta Was this translation helpful? Give feedback.
-
@steinarb Wait! Why did you attach your question on different discussion?!?!? What the... Why did you not START A NEW DISCUSSION?!?!?!! Now we have two unrelated threads -- just because you thought original might be somehow related does not mean you should hi-jack it. This is ridiculous, closing this Discussion: feel free to open new one, will not continue on this mixed-up monster. |
Beta Was this translation helpful? Give feedback.
-
I have added all details in this question in stack overflow.
https://stackoverflow.com/questions/77122208/jackson-custom-annotation-for-field-level-deserialization
In my case the incoming json is less jackson friendly. It has nested objects, plus the field names are different. Also, information of one nested object needs to be set on multiple object values of a Person class. (OrderRequest in the below example json).
Tech stack
Is there a way to achieve this ? The fact that it needs to work with Flux and Mono (webflux).
After this attempt, I have already wrote multiple custom deserializers. Jackson fails for invoke custom deserializer.
ProductDeserializer and AddressDeserializer never invoked in unit test.
Input json sample
Java classes
Beta Was this translation helpful? Give feedback.
All reactions