I would suggest you will try to read my first response again... but
since it was of no help somehow, I'll try again.
Without stack trace to NPE, I may be wrong (why not include the stack
trace? That would be the single most useful
piece of information... :-D ), but I know that this line:
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
can only give you non-null mapper if the parser has reference to one.
There is no guarantee for this; it depends on
who creates said JsonParser. If mapper creates fine, fine, it passes itself.
But since you are constructing a nested parse here:
return mapper.readValue(root.traverse(), variantClass);
-- as `root.traverse()` creates a JsonParser that iterates over
contents of a tree (JsonNode) -- where does that parser get
the ObjectMapper? JsonNodes do not keep refernce to ObjectMapper (they
don't own, nor should retain linkage), and as such,
So.... it doesn't.
If you choose to rely on "codec" being accessible from JsonParser, you
must ensure this occurs.
And the easiest thing is to change code to:
return mapper.readValue(root.traverse(mapper), variantClass);
which avoids NPE, iff that was due to missing codec binding.
-+ Tatu +-
On Fri, Jul 20, 2018 at 3:03 PM, Michal Aron <[email protected]> wrote:
> Hey, thanks for your time for this! You could find deserializer in
> stackoverflow thread I mentioned. Let me sum up it here is well.
>
> Not sure if I completely understood your point though..
>
> So the deserializer for the wrapper would look like this:
> public class WrapperDeserializer extends StdDeserializer<WrapperDTOAbstract>
> {
>
>
> public WrapperDeserializer() {
> super(WrapperDTOAbstract.class);
> }
>
>
> @Override
> public WrapperDTOAbstract deserialize(JsonParser jp,
> DeserializationContext ctxt) throws IOException, JsonProcessingException {
> Class<? extends WrapperDTOAbstract> variantClass = null;
> ObjectMapper mapper = (ObjectMapper) jp.getCodec();
> ObjectNode root = mapper.readTree(jp);
> JsonNode node;
> node = root.get("dto");
> if (node != null) {
> switch (node.getNodeType()) {
> case STRING:
> variantClass = WrapperDTOString.class;
> break;
>
> case OBJECT:
> variantClass = WrapperDTOObject.class;
> break;
>
> case ARRAY:
> variantClass = WrapperDTOList.class;
> break;
> }
> }
> if (variantClass == null) {
> return null;
> }
> return mapper.readValue(root.traverse(), variantClass);
> }
> }
>
> public class ImplDTODeserializer extends StdDeserializer<AbstractDTO> {
>
> public ImplDTODeserializer() {
> super(AbstractDTO.class);
> }
>
> @Override
> public AbstractDTO deserialize(JsonParser jp, DeserializationContext
> ctxt) throws IOException, JsonProcessingException {
> Class<? extends AbstractDTO> variantClass = null;
> ObjectMapper mapper = (ObjectMapper) jp.getCodec();
> ObjectNode root = mapper.readTree(jp);
> JsonNode node;
> node = root.get("member");
> if (node != null) {
> switch (node.getNodeType()) {
> case OBJECT:
> variantClass = Impl1DTO.class;
> break;
>
> case ARRAY:
> variantClass = Impl2DTO.class;
> break;
> }
> }
> if (variantClass == null) {
> return null;
> }
> return mapper.readValue(root.traverse(), variantClass);
> }
> }
>
> Its registration is done in java spring config as following:
> WrapperDeserializer wrapperDeserializer = new WrapperDeserializer();
> SimpleModule wrapperModule = new
> SimpleModule("PolymorphicWrapperDeserializerModule");
> WrapperModule.addDeserializer(WrapperDTOAbstract.class,
> wrapperDeserializer);
> objectMapper.registerModule(wrapperModule);
>
> ImplDeserializer implDeserializer = new ImplDeserializer();
> SimpleModule implModule = new
> SimpleModule("PolymorphicImplDeserializerModule");
> ImplModule.addDeserializer(ImplDTOAbstract.class, implDeserializer);
> objectMapper.registerModule(implModule);
>
> And my interpretation how it works was that when I return it on the root
> entity (Wrapper here) as return mapper.readValue(root.traverse(),
> variantClass); it will continue to traverse the node but still with respect
> to the object schema. So it will continue deserializing until it finds
> AbstractDTO declaration and will use its deserializer to follow up from that
> node down its tree..
>
> Sorry if I sound a bit confused, but this makes only sense to me how it
> should work the most elegant way - but certainly I miss something here :/
>
> Thanks, Michal
>
> On Friday, 20 July 2018 22:02:11 UTC+2, Tatu Saloranta wrote:
>>
>> On Fri, Jul 20, 2018 at 5:06 AM, Michal Aron <[email protected]> wrote:
>> > Hey,
>> >
>> > May somebody please have a look at the following problem?
>> >
>> > It is about nested polymorphism into another object and it fails on
>> > nullPointerException when it meets attribute using abstract class
>> > declaration.
>> >
>> > In other words it fails to determine which concrete entity to use when
>> > parent entity for that attribute is using abstract entity.Or it never
>> > does -
>> > since it never enter the deserializer.
>> >
>> > Problem:
>> > 1) I am acting as a proxy, getting requests from UI and forwarding them
>> > to
>> > the other system.
>> > 2) Before sending the response back to the UI I am mapping it to my
>> > local
>> > DTO schema of that remote system – so we can have power over what we
>> > send to
>> > the UI..
>> > 3) That system sends completely ambiguous data structure depending on
>> > their
>> > count. Imagine entity object with attribute member. If that member is
>> > only
>> > one, that attribute is object. If more than one, then that attribute is
>> > array.
>> >
>> > So you see my point now.. I have 2 DTOs for this case – 1 is when member
>> > is
>> > object, 2 is when member is array and they both inherit from abstract
>> > parent.
>> >
>> > But this isnt all.. That schema I just described is wrapped into another
>> > Wrapper object where I am using their abstract parent for the attribute
>> > type. That wrapper is needed because that remote system returns 0 or 1
>> > or >1
>> > objects in the very same attribute of that wrapper but of different type
>> > also. So it looks like this:
>> >
>> > public class Wrapper {
>> > @JsonProperty(“wrapper”)
>> > private AbstractWrapper wrapper;
>> > }
>> >
>> > public abstract class AbstractWrapper {
>> > }
>> >
>> > public class WrapperString extends AbstractWrapper {
>> > @JsonProperty(“dto”)
>> > private String dto;
>> > }
>> >
>> > public class WrapperObject extends AbstractWrapper{
>> > @JsonProperty(“dto”)
>> > private AbstractDTO dto;
>> > }
>> >
>> > public class WrapperList extends AbstractWrapper{
>> > @JsonProperty(“dto”)
>> > private List<AbstractDTO> dto;
>> > }
>> >
>> > public abstract class AbstractDTO {
>> > }
>> >
>> > public class Impl1DTO extends AbstractDTO {
>> > @JsonProperty(“member”)
>> > private String member;
>> > }
>> >
>> > public class Impl2DTO extends AbstractDTO {
>> > @JsonProperty(“member”)
>> > private List<String> member;
>> > }
>> >
>> >
>> > I have both deserializers for this registered. It will check if member
>> > is
>> > type of Object or Array or String and determine the concrete class. This
>> > works for the Wrapper Polymorphism. However it doesnt for nested
>> > polymorphism. It never enters that deserializer no matter what I try..
>> > And
>> > when deserializing it fails on null pointer exception on
>> > @JsonProperty(“dto”)
>> >
>> > Here is also more detailed stackoverflow thread I created for this.
>> >
>> > I will appreciate any inputs for this. Thanks, Michal
>>
>>
>> Although code is missing some of the relevant code (how is the
>> deserializer registered f.ex),
>> as well as actual stack trace (what is null?), I'd be guessing that this:
>>
>> return mapper.readValue(root.traverse(), variantClass);
>>
>> might be it. Specifically, `root.traverse()` creates `JsonParser`, but
>> not one with codec assigned (where would it get one?).
>> But I think there is another variant that takes `codec`, or, if not,
>> simply call `setCodec()` on parser. And that would make sure
>> you at least are not missing codec (ObjectMapper).
>>
>> But wait! There's more. Instead of relying on codec, much/most of the
>> functionality is actually available via DeserializationContext!
>> For example, it's possible to call `readValue()` there to read
>> `JsonNode`, something like
>>
>> JsonNode tree = ctxt.readValue(p, JsonNode.class);
>>
>> and similarly for other types. This is more efficient way (it avoids
>> setting up of context), and retains some more of contextual
>> information
>> (any attributes you may have set).
>>
>> -+ Tatu +-
>
> --
> 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 [email protected].
> To post to this group, send email to [email protected].
> For more options, visit https://groups.google.com/d/optout.
--
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 [email protected].
To post to this group, send email to [email protected].
For more options, visit https://groups.google.com/d/optout.