Why a Single Log Line Crashed My Java Service: Fastjson Getter Pitfalls Explained
During a recent iteration, adding a simple log statement triggered a system exception because Fastjson automatically invoked a custom getter that returned null, leading to an NPE; the article details the root cause, the serialization process, and practical solutions to prevent similar issues.
1. Problem Discovery
During an iteration, a newly added log statement caused the main workflow to throw an exception. The log code was:
log.info("batch save receipt context:{}", JSON.toJSONString(context));The error log screenshot showed a Fastjson JSONException caused by a null field during serialization.
2. Solution
Investigation revealed a newly added getDutyLevelNumber() method that processed a field without null‑checking. Fastjson automatically calls getter methods during serialization, so the null field caused an NPE. Renaming the method to fetchDutyLevelNumber() stopped Fastjson from invoking it and resolved the issue.
To avoid similar problems, you can:
Rename custom getters to avoid the get prefix.
Use SerializerFeature.IgnoreNonFieldGetter when calling toJSONString.
Annotate getters with @JSONField(serialize = false) to exclude them.
Centralize JSON conversion in a utility class that handles exceptions.
3. Serialization Process
Fastjson’s serialization starts with JSON.toJSONString:
public static String toJSONString(Object object, int defaultFeatures, SerializerFeature... features) {
SerializeWriter out = new SerializeWriter((Writer) null, defaultFeatures, features);
try {
JSONSerializer serializer = new JSONSerializer(out);
serializer.write(object);
String outString = out.toString();
int len = outString.length();
if (len > 0 && outString.charAt(len - 1) == '.' && object instanceof Number && !out.isEnabled(SerializerFeature.WriteClassName)) {
return outString.substring(0, len - 1);
}
return outString;
} finally {
out.close();
}
}The JSONSerializer.write method obtains an ObjectSerializer for the object's class and delegates the actual conversion:
public final void write(Object object) {
if (object == null) {
out.writeNull();
return;
}
Class<?> clazz = object.getClass();
ObjectSerializer writer = getObjectWriter(clazz);
try {
writer.write(this, object, null, null, 0);
} catch (IOException e) {
throw new JSONException(e.getMessage(), e);
}
}The serializer is retrieved via SerializeConfig.getObjectWriter, which may create a JavaBeanSerializer for custom objects. JavaBeanSerializer uses TypeUtils.buildBeanInfo to collect field information, and TypeUtils.computeGetters to turn getter methods into FieldInfo entries. During serialization, each FieldInfo invokes its associated getter, which is why getDutyLevelNumber() was executed and caused the NPE.
4. Practical Summary
When using Fastjson:
Avoid custom getters that can be invoked unintentionally; rename them or exclude them with annotations.
Prefer field‑based serialization if getters are not needed.
Be aware that Fastjson follows the JavaBeans convention, calling any method that starts with get or is.
Consider the small performance cost of method calls versus the safety and flexibility they provide.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Alibaba Cloud Developer
Alibaba's official tech channel, featuring all of its technology innovations.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
