Why Does FastJSON Invoke isChinaName()? Uncovering Hidden Serialization Pitfalls
A seemingly harmless log addition triggered a cascade of FastJSON serialization calls, leading to a NullPointerException caused by the isChinaName() method, and the article walks through the debugging process, explains the underlying serialization mechanics, and proposes a clean solution using @JSONField(serialize=false).
Recently a simple log line was added before a nightly release, but after deployment a flood of alerts forced an immediate rollback. The root cause was a NullPointerException during FastJSON serialization, where the method isChinaName() was invoked on a null country field.
Scenario Reconstruction
Defined a CountryDTO class with a String country field, standard getter/setter, and an isChinaName() method that compares country to "中国".
public class CountryDTO {
private String country;
public void setCountry(String country) { this.country = country; }
public String getCountry() { return this.country; }
public Boolean isChinaName() { return this.country.equals("中国"); }
}Test class FastJonTest creates a CountryDTO , serializes it with JSON.toJSONString , and prints the result.
public class FastJonTest {
@Test
public void testSerialize() {
CountryDTO countryDTO = new CountryDTO();
String str = JSON.toJSONString(countryDTO);
System.out.println(str);
}
}The runtime error shows that during serialization isChinaName() was executed, but this.country was null, causing the NPE.
Source Code Analysis
Debugging revealed that FastJSON generates a dynamic class ASMSerializer_1_CountryDTO via ASM to replace reflection. The write() method of JavaBeanSerializer is the entry point.
Inside JavaBeanSerializer, getObjectWriter() eventually calls SerializeConfig#createJavaBeanSerializer, which uses TypeUtils.computeGetters to collect getter methods.
public static List<FieldInfo> computeGetters(Class<?> clazz, ... ) {
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 && !annotation.serialize()) continue;
String methodName = method.getName();
if (methodName.startsWith("get")) { /* treat as getter */ }
if (methodName.startsWith("is")) { /* treat as boolean getter */ }
}
}The algorithm classifies methods into three cases that affect serialization:
@JSONField(serialize = false, name = "xxx") annotation
Methods starting with get Methods starting with is Because isChinaName() matches the is pattern and returns a Boolean, FastJSON includes it in the serialization process, leading to the NPE.
JavaBeanSerializer Serialization Principle
The serializer ultimately calls JavaBeanSerializer.write(), which iterates over the collected getters and writes their values to the JSON output.
Serialization Flowchart
Example Code
Four representative cases are demonstrated:
/**
* case1: @JSONField(serialize = false)
* case2: getXxx() returns void
* case3: isXxx() returns non‑boolean
* case4: @JSONType(ignores = "xxx")
*/
@JSONType(ignores = "otherName")
public class CountryDTO {
private String country;
public void setCountry(String country) { this.country = country; }
public String getCountry() { return this.country; }
public static void queryCountryList() { System.out.println("queryCountryList() executed!!"); }
public Boolean isChinaName() { System.out.println("isChinaName() executed!!"); return true; }
public String getEnglishName() { System.out.println("getEnglishName() executed!!"); return "lucy"; }
@JSONField(serialize = false)
public String getOtherName() { System.out.println("getOtherName() executed!!"); return "lucy"; }
@JSONField(serialize = false)
public void getEnglishName3() { System.out.println("getEnglishName3() executed!!"); }
@JSONField(serialize = false)
public String isChinaName2() { System.out.println("isChinaName2() executed!!"); return "isChinaName2"; }
}Running the test prints:
isChinaName() executed!!
getEnglishName() executed!!
{"chinaName":true,"englishName":"lucy"}Code Guidelines
To avoid ambiguous serialization rules, explicitly mark methods that should not participate using @JSONField(serialize = false). This makes the intent clear and prevents accidental NPEs.
High‑Frequency Serialization Scenarios
The article follows a logical flow: problem discovery → principle analysis → solution implementation → coding standards, illustrating how a single issue can lead to broader best‑practice recommendations.
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.
Java Interview Crash Guide
Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.
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.
