Matt’s Tidbits #9 — Be careful with JSON deserialization
I was on vacation last week and posted about the benefits of taking a break and seeing things from a different perspective. This time, I want to warn you about an issue to be aware of with JSON deserialization.
One of the most widely-used JSON deserializers for Java is Jackson. This very powerful library offers lots of tools to help you efficiently convert between Java and JSON, with support for advanced operations such as handling bidirectional references, sophisticated dynamic type mapping, and more!
Jackson has support for a “strict parsing mode”, which will throw an exception during parsing if the POJO does not exactly match the JSON. This can be achieved by setting a flag on your ObjectMapper:
This can be a very useful tool, and I’ve commonly set this up so it uses strict parsing in debug builds so I can easily detect when the backend has changed its network responses.
However, using this can produce some interesting behaviors. For example, consider the following POJO:
It should be easy to imagine that someUnusedField
might only be used within this package (or not at all). This will cause a lint warning to appear next to this line, and Android Studio will provide you with a “quick fix” hint to make this field package protected or remove it entirely.
If you, like me, despise lint warnings, you may be tempted to take Android Studio’s suggestion and “fix” this. However, what you may encounter next is random crashes of your app — but only in certain cases. The reason for this variable behavior is the FAIL_ON_UNKNOWN_PROPERTIES
flag which may only be enabled for debug builds.
In its basic mode of operation, Jackson requires that fields be public* in order to properly serialize/deserialize them. So, if the visibility is package-protected, it means that it can’t deserialize that field from JSON to the POJO — and, when the FAIL_ON_UNKNOWN_PROPERTIES
flag is set, Jackson can’t findsomeUnusedField
in your POJO and throws an exception.
How do we solve this? One option of course is to do nothing, but then the lint warnings will still appear in your code. I have gotten into the habit of adding a @SuppressWarnings(“WeakerAccess”)
annotation to all of my data classes to prevent myself (or other developers) from changing the visibility of fields in the JSON just to fix lint warnings. This has the advantage of still showing a warning when a property is unused. But, what’s the right way to handle an unused property? Just removing it from the POJO will cause the app to crash when run in debug mode. We could disable the FAIL_ON_UNKNOWN_PROPERTIES
flag, but then it’s no longer warning you of changes to the JSON from the backend. You could add a @SuppressWarnings("unused")
annotation, but I find that less useful in this case — then Android Studio will no longer tell you if the field is unused.
Instead, my preference is to use a @JsonIgnoreProperties
annotation, like this:
This prevents the field from actually existing in your POJO (since you’re not using it), will let you keep the strict parsing so you detect any newly added/removed fields, and be able to detect when you’re no longer using a particular field in Android Studio.
You may have noticed the asterisk I added next to the word public
earlier in this post — that’s because there are actually multiple ways that Jackson can infer whether a property should be serialized/deserialized or not: https://www.baeldung.com/jackson-field-serializable-deserializable-or-not
And, here’s some additional documentation on handling unknown properties: https://www.baeldung.com/jackson-deserialize-json-unknown-properties
Do you have a different way that you prefer to handle JSON parsing? Leave a comment below! And, please follow me on Medium if you’re interested in being notified of future tidbits.
This tidbit was originally delivered on 2/2/2018.