Deserializing @JsonUnwrapped fields with @JsonCreator annotated constructors

3,431 views
Skip to first unread message

victo...@gmail.com

unread,
Jan 11, 2017, 2:05:51 PM1/11/17
to jackson-user
Hi,

I am familiar with the issues https://github.com/FasterXML/jackson-databind/issues/265 (i.e., using @JsonUnwrapped within a @JsonCreator) and others, but in the end, I couldn't find the correct way of using @JsonUnwrapped with @JsonCreator (i.e., I want to avoid having mutable objects whose fields are modified by Jackson).

If we take the example from the wiki:

class Location {
    public final int latitude, longitude;

public Location(int latitude, int longitude) { ... }
}

class Place {
    public final String name;
    @JsonUnwrapped public final Location location;

@JsonCreator
public Place(@JsonProperty("name") String name, ????) { ... }
}

What is the correct way of defining the Place constructor?

If I use:

    @JsonCreator
public Place(@JsonProperty("name") String name,
@JsonProperty("latitude") int latitude,
@JsonProperty("longitude") int longitude) {
this.name = name;
this.location = new Location(latitude, longitude);
}

Then I get an error from Jackson because it tries, after SUCCESSFULLY creating the Place object including its location, to then set the @JsonUnwrapped annotated fields and fails because it can't create Location, and even if it could, it has already consumed the stream so it can't read the proper values for location.

If I use (expecting Jackson to set the location field using the rest of the information from the stream):

    @JsonCreator
public Place(@JsonProperty("name") String name) {
this.name = name;
this.location = null;
}

Then it also fails because I'm not sure why it can't read the rest of the stream (I didn't investigate this too much because I got fed up and thought I would come asking here :).

So is there a way to make that work and how?

I am using Jackson 2.7.8 for now because of dropwizard integration, but I should be able to use a newer version maybe…

Thank you very much!

Tatu Saloranta

unread,
Jan 11, 2017, 3:53:14 PM1/11/17
to jackson-user
Consumption is not a problem (or, at least, shouldn't), since
buffering will be used automatically for all @JsonUnwrapped fields.

> If I use (expecting Jackson to set the location field using the rest of the
> information from the stream):
>
> @JsonCreator
> public Place(@JsonProperty("name") String name) {
> this.name = name;
> this.location = null;
> }
>
>
> Then it also fails because I'm not sure why it can't read the rest of the
> stream (I didn't investigate this too much because I got fed up and thought
> I would come asking here :).
>
> So is there a way to make that work and how?
>
> I am using Jackson 2.7.8 for now because of dropwizard integration, but I
> should be able to use a newer version maybe…

My preferred course would be to just not trying to handle `latitude`
and `longitude`, and instead have a setter for location.
That should work fine; that is, constructor only taking `name`.

Otherwise removing of `@JsonUnwrapped` annotation should work, and you
could instead define something like:

@JsonAnyGetter
public Map<String> stuff() {
// construct Map with `latitude` and `longitude`
return mapWithLatLong;
}

I am not sure if it'd be possible to make your attempt work: challenge
is that deserializer for Place really does not have much information
on what is contained within @JsonUnwrapped pojo; handling is too
indirect.
So there is nothing linking properties lat/long passed to creator, and
contents of `Location`, from deserializer perspective.

Still... if you could file an issue, perhaps something could be done
for special case where all properties are handled, so there is nothing
to hand to @JsonUnwrapped handlers, call would be avoided and everyone
would be happy.
This is not fool-proof, even if so, since current handling of
unwrapped values combines ALL potential properties (as they can not be
separated); but could be an improvement.

-+ Tatu +-

victo...@gmail.com

unread,
Jan 12, 2017, 5:49:35 AM1/12/17
to jackson-user
The only thing that successfully worked is to have a parameter-less constructor and let Jackson set the fields (I didn't use a setter but simply let jackson access the private field via reflection I guess).

I thought it should at least worked with the first case (i.e. if there is a constructor for the object, then Jackson should not try to set @JsonUnwrapped fields). I will open an issue for that then.

The second case, even though you said should work (I think that's what you meant by "not trying to handle `latitude`
and `longitude`, and instead have a setter for location."), does not as I explained… not sure if it is so easy for Jackson to partially build an object using a @JsonCreator constructor… I will make note of that in the issue I create.

Of course not using @JsonUnwrapped is not an option here :)

Thanks for your help in any case!

victo...@gmail.com

unread,
Jan 12, 2017, 6:05:49 AM1/12/17
to jackson-user

Tatu Saloranta

unread,
Jan 12, 2017, 4:13:56 PM1/12/17
to jackson-user
On Thu, Jan 12, 2017 at 2:49 AM, <victo...@gmail.com> wrote:
> The only thing that successfully worked is to have a parameter-less
> constructor and let Jackson set the fields (I didn't use a setter but simply
> let jackson access the private field via reflection I guess).
>
> I thought it should at least worked with the first case (i.e. if there is a
> constructor for the object, then Jackson should not try to set
> @JsonUnwrapped fields). I will open an issue for that then.

Yes but do note that as far as databind is concerned there is no
relationship between "unwrapped" property with logical name of
'location' (but not used for anything), and then separate properties
'latitude' and 'longitude' passed to creator. This because contents of
unwrapped POJO can not be inserted at this point -- this is an
implementation limitation and not hard limit, of course, but
nonetheless current active limit.

In a way it could work if there was a way to indicate that
`@JSonUnwrapped` only relates to serialization, that is, ignored for
deserialization.
Or, eventually, if and when unwrapped-handling is rewritten (it really
ought to: only limit is time and interested), this could be fixed
properly.

> The second case, even though you said should work (I think that's what you
> meant by "not trying to handle `latitude`
> and `longitude`, and instead have a setter for location."), does not as I
> explained… not sure if it is so easy for Jackson to partially build an
> object using a @JsonCreator constructor… I will make note of that in the
> issue I create.

Right: I can see why it does not work currently, but it seems that
this particular case could be made to work without user-side changes.
But no such improvement has been made yet.

>
> Of course not using @JsonUnwrapped is not an option here :)
>
> Thanks for your help in any case!

Likewise!

-+ 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 jackson-user...@googlegroups.com.
> To post to this group, send email to jackso...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages