Why Adding a Simple Log Triggered FastJSON Serialization Errors – A Deep Dive
A recent deployment added a log line that caused FastJSON to invoke unexpected methods during serialization, leading to a NullPointerException; this article reconstructs the scenario, analyzes the FastJSON internals, and offers best‑practice annotations to prevent similar bugs.
Online Incident Review
Recently a simple log statement was added to the review method before deployment, which triggered a flood of alerts after the release. The code was rolled back and the log line removed.
Scenario Reconstruction
A CountryDTO class was defined with fields and methods, and a test class FastJonTest serialized an instance using FastJSON.
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("中国"); }
} public class FastJonTest {
@Test
public void testSerialize() {
CountryDTO countryDTO = new CountryDTO();
String str = JSON.toJSONString(countryDTO);
System.out.println(str);
}
}During execution a NullPointerException occurred because isChinaName() was invoked while this.country was null.
Why does serialization invoke isChinaName()?
What other methods are called during FastJSON serialization?
Source Code Analysis
Debugging revealed that FastJSON uses ASM to generate a serializer class ASMSerializer_1_CountryDTO, which implements the write() method of JavaBeanSerializer.
ASM is used to replace Java reflection with dynamically generated bytecode, reducing overhead.
JavaBeanSerializer Serialization Principle
The write() method of JavaBeanSerializer obtains an ObjectWriter via getObjectWriter(), which eventually calls SerializeConfig#createJavaBeanSerializer and TypeUtils#computeGetters.
public static List<FieldInfo> computeGetters(Class<?> clazz, JSONType jsonType,
Map<String,String> aliasMap, Map<String,Field> fieldCacheMap,
boolean sorted, PropertyNamingStrategy propertyNamingStrategy) {
// ... iterate over methods ...
if (method.getReturnType().equals(Void.TYPE)) { continue; }
if (method.getParameterTypes().length != 0) { continue; }
// handle @JSONField, getXxx, isXxx, etc.
}The method selection follows three main rules: @JSONField(serialize = false, name = "xxx") annotation.
Methods starting with get.
Methods starting with is.
Serialization Flowchart
Example Code and Best Practices
Using @JSONField(serialize = false) explicitly marks methods that should not participate in serialization, making the code easier to read and maintain.
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() executed!!");
}
@JSONField(serialize = false)
public Boolean isChinaName() {
System.out.println("isChinaName() executed!!");
return true;
}
// other getters/setters with or without @JSONField
}Adopting this convention reduces variance among team members and prevents accidental serialization of unwanted methods.
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.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
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.
