So I complain. I think it could be done better but I didn’t do any research as why this is the default behavior of Jackson so you can educate me the whole reason behind this design. Since Jackson is the json library SpringMVC has picked, I’m using it.
By default, Jackson will convert any
- public method
- starts with “get”
- takes no arguments
- returns a value
from a POJO to json.
Is it not smart enough to figure out how many of those method are actually truly getters? And how many of them may be convenience methods?
Here’s an example:
public class Foo { private List<String> messsages = new ArrayList<String>(); public List<String> getMesssages() { return messsages; } public void setMesssages(List<String> messsages) { this.messsages = messsages; } public String getLastMessage() { if(getMesssages().isEmpty()) { return ""; } else { return getMesssages().get(getMesssages().size()-1); } } }
@Test public void test() throws JsonGenerationException, JsonMappingException, IOException { ObjectMapper mapper = new ObjectMapper(); Foo foo = new Foo(); String jsonFoo = mapper.writeValueAsString(foo); System.out.println(jsonFoo); Foo convertedFoo = mapper.readValue(jsonFoo, Foo.class); }
This test fails because
org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "lastMessage" (Class net.pureessence.Foo), not marked as ignorable
You have some options to get around this issue of course.
Exclude the convenience method so Jackson will NOT convert it
@JsonIgnoreProperties({ "lastMessage" }) public class Foo { ...
or
... @JsonIgnore public String getLastMessage() { ...
Pick which properties Jackson should INCLUDE in its conversion
@JsonPropertyOrder({ "messages" }) @JsonAutoDetect(value=JsonMethod.FIELD) public class Foo { @JsonProperty private List<String> messsages = new ArrayList<String>(); ...
I’m sure there are other options I have not mentioned that you may use to get around the issue.
Either way it requires your close attention and care and most likely tests of your exact json conversion to help you pay attention to how your changes on the POJO will affect your json conversion. I wish the default behavior of Jackson would just convert real properties only. Such as something that have both getters & setters. More here and here.
How would Jackson determine “convenience methods” from accessors? By assuming there must be a matching field? This won’t work if names differ (many developers use prefix or suffix for names, just as an example). Rules for things like this can be complicated, so it may not be worth the trouble trying to read developers mind…
For what it is worth, discovery uses standard Java Bean model of determining getters and setters, although it does not require existence of both (unless you configure it to — there is a feature for that)
Note that you can use @JsonIgnore on getter as well.
You could also consider changing your naming convention, so that convenience methods would not use names like getXxx() (“public String lastMessage() { }”). Just an idea.
I tried @JsonIgnore on the getter, it did not work for me. I will try again.
Yes I also tried changing my naming convention but it’s not natural to fight against what you are used to. You forget that sooner than you believe. That’s why I started adding tests against my json conversion STRING. That’s the only way it will catch this kind of issue for me before I release my change to production when I’m not paying attention.
Yes I think convention over configuration in this case will be great! That’s the general rule of thumb of Spring, isn’t it?
Thanks for the quick reply.
Oh btw, if I use the same POJO jackson converts in my view layer (which I think it’s a fair design) to be used by JSTL, the changing naming convention no longer applies because JSTL EXPECTS any property you want it to access to have a public “get” method.
Personally I think JSTL’s expectation of this “get” method CONVENTION is more natural.
Hi, how about add to your default conversion condition: there must exist a public void “set” method with the same name as the “get” method that takes one argument? I mean this kind of “mind reading” is not really out of the blue as I hope most of the getters & setters are generated by IDEs.
Fortunately, such a setting (to require setter and getter) already exists, see [http://fasterxml.github.com/jackson-databind/javadoc/2.0.5/com/fasterxml/jackson/databind/MapperFeature.html#REQUIRE_SETTERS_FOR_GETTERS]
So you need to configure ObjectMapper with something like:
mapper.enable(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS);
// above for Jackson 2.0; or for Jackson 1.9:
mapper.enable(SerializationConfig.Feature.REQUIRE_SETTERS_FOR_GETTERS);
and from thereon, “orphan” getters will be ignored.
The reason this is not the default is just that for other use cases this would require additional annotations: immutable POJOs, for example, never have setters (nor mutable public fields). FWIW, Jackson works such beans as well (using @JsonCreator to indicate constructor to use).
JSTL is a good data point I think: like you said, anything of form “getXxx()” is considered a property. Which is what Jackson defaults to as well.
Hope this helps!
Hi this is a very good news.
Thanks!