Understanding FastJSON JavaBean Serialization and Common Pitfalls
This article analyzes a Java serialization issue caused by FastJSON invoking a getter on a null field, explains the underlying ASM‑based serializer generation, and provides best‑practice code examples and annotations to prevent similar bugs in backend development.
Author "Lei Ge" shares a recent incident where adding a simple log line caused a cascade of alerts after deployment, leading to a rollback.
Investigation revealed that during FastJSON serialization of a CountryDTO object, the method isChinaName() was invoked while the country field was null, resulting in a NullPointerException.
The article explains how FastJSON dynamically generates a serializer class (e.g., ASMSerializer_1_CountryDTO ) using ASM bytecode generation to avoid reflection overhead, and how the JavaBeanSerializer writes the object by invoking getter methods that match certain patterns.
Three categories of methods are considered during serialization: annotated with @JSONField(serialize = false) , getters prefixed with get , and boolean getters prefixed with is . The source code of computeGetters in FastJSON is examined to show how these rules are applied.
public static List<FieldInfo> computeGetters(Class<?> clazz, //
JSONType jsonType, //
Map<String,String> aliasMap, //
Map<String,Field> fieldCacheMap, //
boolean sorted, //
PropertyNamingStrategy propertyNamingStrategy ){
// ...
Method[] methods = clazz.getMethods();
for(Method method : methods){
if(method.getReturnType().equals(Void.TYPE)){
continue;
}
if(method.getParameterTypes().length != 0){
continue;
}
JSONField annotation = TypeUtils.getAnnotation(method, JSONField.class);
if(annotation != null){
if(!annotation.serialize()){
continue;
}
if(annotation.name().length() != 0){
// ...
}
}
if(methodName.startsWith("get")){
// ...
}
if(methodName.startsWith("is")){
// ...
}
}
// ...
}A flowchart of the serialization process is provided, followed by a comprehensive example class CountryDTO that demonstrates various cases such as ignored fields, void getters, non‑boolean is methods, and the use of @JSONType and @JSONField annotations.
public class CountryDTO {
private String country;
public void setCountry(String country) {
this.country = country;
}
public String getCountry() {
return this.country;
}
@JSONField(serialize = false)
public static void queryCountryList() {
System.out.println("queryCountryList()执行!!");
}
public Boolean isChinaName() {
System.out.println("isChinaName()执行!!");
return true;
}
public String getEnglishName() {
System.out.println("getEnglishName()执行!!");
return "lucy";
}
@JSONField(serialize = false)
public String getOtherName() {
System.out.println("getOtherName()执行!!");
return "lucy";
}
@JSONField(serialize = false)
public String getEnglishName2() {
System.out.println("getEnglishName2()执行!!");
return "lucy";
}
@JSONField(serialize = false)
public void getEnglishName3() {
System.out.println("getEnglishName3()执行!!");
}
@JSONField(serialize = false)
public String isChinaName2() {
System.out.println("isChinaName2()执行!!");
return "isChinaName2";
}
}Running the example produces output showing which methods are executed and the final JSON string, e.g., {"chinaName":true,"englishName":"lucy"} . The author recommends explicitly marking methods that should not be serialized with @JSONField(serialize = false) to improve code clarity and avoid similar bugs.
Additional sections list high‑frequency serialization scenarios and include visual diagrams of the call chain and process flow.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
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.