Why Does FastJSON Invoke isChinaName() During Serialization? A Deep Dive
The article recounts a production incident caused by a simple log addition, then analyzes FastJSON’s serialization process, explaining why methods like isChinaName() are invoked, how ASM-generated serializers work, and offers best‑practice guidelines using @JSONField(serialize = false) to control serialization.
Online Incident Review
Earlier a very simple feature was added; before the evening release a log line was inserted to reflect the company's values. The line seemed harmless, but after deployment a flood of alerts appeared, prompting an immediate rollback and removal of the log code.
Scenario Reproduction
Define a CountryDTO class.
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("中国");
}
}Define a test class FastJonTest .
public class FastJonTest {
@Test
public void testSerialize() {
CountryDTO countryDTO = new CountryDTO();
String str = JSON.toJSONString(countryDTO);
System.out.println(str);
}
}Running the test throws a NullPointerException because during serialization the method isChinaName() is executed while this.country is still null.
Why does serialization invoke isChinaName()?
What other methods are executed during serialization?
Source Code Analysis
Debugging reveals the call chain ending at ASMSerializer_1_CountryDTO.write, which is generated by FastJSON using ASM to avoid reflection overhead.
ASM technology can dynamically generate classes to replace Java reflection, reducing repeated reflection costs.
JavaBeanSerializer Serialization Principle
Serialization mainly calls JavaBeanSerializer.write(). JavaBeanSerializer obtains a writer via getObjectWriter(), which eventually calls
com.alibaba.fastjson.serializer.SerializeConfig#createJavaBeanSerializerand com.alibaba.fastjson.util.TypeUtils#computeGetters.
public static List<FieldInfo> computeGetters(Class<?> clazz, JSONType jsonType, Map<String,String> aliasMap, Map<String,Field> fieldCacheMap, boolean sorted, PropertyNamingStrategy propertyNamingStrategy) {
// ... omitted code ...
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) {
// ... omitted code ...
}
}
if (methodName.startsWith("get")) {
// ... omitted code ...
}
if (methodName.startsWith("is")) {
// ... omitted code ...
}
}
}The logic can be summarized into three cases: @JSONField(serialize = false, name = "xxx") annotation getXxx() getter methods isXxx() boolean getters
Serialization Flowchart
Example Code
/**
* 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()执行!!");
}
public Boolean isChinaName() {
System.out.println("isChinaName()执行!!");
return true;
}
public String getEnglishName() {
System.out.println("getEnglishName()执行!!");
return "lucy";
}
public String getOtherName() {
System.out.println("getOtherName()执行!!");
return "lucy";
}
@JSONField(serialize = false)
public String getEnglishName2() {
System.out.println("getEnglishName2()执行!!");
return "lucy";
}
public void getEnglishName3() {
System.out.println("getEnglishName3()执行!!");
}
public String isChinaName2() {
System.out.println("isChinaName2()执行!!");
return "isChinaName2";
}
}Running the above produces:
isChinaName()执行!!
getEnglishName()执行!!
{"chinaName":true,"englishName":"lucy"}Code Standards
Serialization rules involve return types, parameter counts, and annotations such as @JSONType and @JSONField. Because team members have varying knowledge, it is advisable to explicitly mark non‑serializable methods with @JSONField(serialize = false) for clarity.
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";
}
}Three Frequently Seen Serialization Cases
The overall process follows: discover the issue → analyze the principle → solve the problem → refine coding standards.
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
