Jackson is either magic or we're using it wrong

92 views
Skip to first unread message

Charles Allen

unread,
Nov 5, 2015, 7:15:15 PM11/5/15
to jackson-user
I came across a situation in our codebase today that I don't understand and would love insight into what's going on. (version 2.4.6 )

in HadoopTuningConfig, there is a property passed via
@JsonCreator

constructor as 

final @JsonProperty("maxRowsInMemory") Integer maxRowsInMemory

It gets re-assigned to a different value in the constructor, and thus the getter for the property looks like this

@JsonProperty
public int getRowFlushBoundary()
{
 
return rowFlushBoundary;
}

Now, the sane interpretation of this (in my understanding) would be a bug whereby the resultant rowFlushBoundary would not get interpreted properly if the object is converted to json then back again.

We have tests for this case which... pass!? I can confirm that the object does indeed serialize to rowFlushBoundary, and when deserializing from the string back into a new instance does indeed have the correct value in the object, even though it was labeled as rowFlushBoundary in the json object

Here are the config options set for the customized ObjectMapper 

 configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
 configure
(MapperFeature.AUTO_DETECT_GETTERS, false);
// configure(MapperFeature.AUTO_DETECT_CREATORS, false); https://github.com/FasterXML/jackson-databind/issues/170
 configure
(MapperFeature.AUTO_DETECT_FIELDS, false);
 configure
(MapperFeature.AUTO_DETECT_IS_GETTERS, false);
 configure
(MapperFeature.AUTO_DETECT_SETTERS, false);
 configure
(SerializationFeature.INDENT_OUTPUT, false);


What sort of magic does Jackson do that I'm not aware of that could cause it to pick up the field even though there are no annotations for it to know how to inject the value?

Thanks for any insight you might have,
Charles Allen

Tatu Saloranta

unread,
Nov 5, 2015, 7:18:18 PM11/5/15
to jackso...@googlegroups.com
It'd be good to get more complete POJO definition, to see what is going on.
Jackson does consider non-public fields as applicable for direct set, in addition to @JsonCreator annotated constructor; constructor is called first with whatever properties are found, and if any are left over, setters and/or fields are used for remainders.

-+ 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.

Ian Barfield

unread,
Nov 5, 2015, 7:34:24 PM11/5/15
to jackso...@googlegroups.com

Neat. The thing that comes to mind is that jackson has settings that both tell it to treat a single JsonProperty annotation to apply to all similarly named properties -- or was that only JsonIgnore only? I think it's the former, but if the latter, there might also be a specific "mutator implies setter" option. In both cases, using JsonGetter instead on that method might fail your test.

Probably the bigger concern is that jackson will (very foolishly, but with the best intentions) try to let you deserialize into final fields after construction. I think that one is even almost certainly enabled by default.

So there's one or two properties that may or may not exist that associates the getter with the field even though it is final to check. You could specialize the annotation. Definitely turn off mutating final fields.

Ian Barfield

unread,
Nov 5, 2015, 7:38:14 PM11/5/15
to jackso...@googlegroups.com

Doc version is old but whatever. Look, both settings are right next to each other! What a cute world.

http://fasterxml.github.io/jackson-databind/javadoc/2.3.0/com/fasterxml/jackson/databind/MapperFeature.html#INFER_PROPERTY_MUTATORS

Charles Allen

unread,
Nov 5, 2015, 8:57:41 PM11/5/15
to jackson-user
That seems to be the answer indeed! Thank you!

Setting either property causes the test to fail in the way expected in the original post.

Now its time to see if such a setting will cause unexpected side effects

Ian Barfield

unread,
Nov 5, 2015, 9:03:57 PM11/5/15
to jackson-user

Almost certainly! I hope in a future major release, jackson will disable writing to final fields by default, but hey, you can always use my wrapper library ;)

Tatu Saloranta

unread,
Nov 5, 2015, 9:15:08 PM11/5/15
to jackson-user
Right, so for sake of completeness, this:

MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS

is the thing to disable if no attempt is to be made to set a final field.
Setting a final field actually works, within some constraints, definition of which I do not fully understand: JVM allows this up to certain synchronization checkpoint, and almost certainly to support default JDK serialization system.

This feature is actually highly valued by many users who want to do immutable objects without having to specify constructors.
And then disliked by others who want to specify constructors... 

Finally, since methods have higher precedence than fields (and constructors higher than either), fields are only used if nothing else is available.

-+ Tatu +-

Ian Barfield

unread,
Nov 5, 2015, 11:39:58 PM11/5/15
to jackson-user

It tends to break pretty badly when certain kinds of references/forks get inlined into methods before being set.

Regardless, those aren't compelling reasons to keep it on by default.


--

Tatu Saloranta

unread,
Nov 6, 2015, 12:47:58 AM11/6/15
to jackso...@googlegroups.com
No, but backwards compatibility is -- there are users who rely on these settings, and if they are changed, I am the one who gets to go through all the bug reports...

-+ Tatu +-

Ian Barfield

unread,
Nov 6, 2015, 12:56:04 AM11/6/15
to jackso...@googlegroups.com

Yeah, just making sure that was still the primary reason. Was why I originally prefixed the possibility of it changing as being part of a major release.

Tatu Saloranta

unread,
Nov 9, 2015, 1:05:11 AM11/9/15
to jackso...@googlegroups.com
Right. There is no philosophical reason, and I agree that it is not a good default due to complexities that may arise. It would be better for users to explicitly enable support, esp. since it may be something that depends on specific JVM in use.

For what it is worth, the feature itself was accidental as well. I just did not check for `final` modifier in the first place, and actually learnt of the feature when someone suggested it's pretty cool you can deserialize into final fields. I was surprised to find it actually works myself...

-+ Tatu +-

Reply all
Reply to author
Forward
0 new messages