Why FastJSON Invokes isChinaName() During Serialization and How to Control It
This article analyzes a FastJSON serialization bug caused by an unexpected call to an isXxx() method, explains the underlying JavaBeanSerializer mechanism, demonstrates how ASM-generated serializers work, and provides coding guidelines with examples to avoid similar issues.
Online Incident Review
Recently a simple feature was added with an extra log line; after deployment a flood of alerts appeared, prompting an immediate rollback and removal of the log statement.
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 the serialization process invokes isChinaName() while this.country is null.
The error reveals that FastJSON calls isChinaName() during serialization, leading to the following questions:
Why does serialization execute isChinaName()?
What other methods are invoked during serialization?
Source Code Analysis
Debugging the call stack shows that FastJSON uses ASM to dynamically generate a class ASMSerializer_1_CountryDTO whose write method performs the serialization.
The ASMSerializer_1_CountryDTO.write method is generated by FastJSON using ASM, which replaces reflection with dynamically generated bytecode to improve performance.
One use case of ASM is to generate classes at runtime to avoid the overhead of Java reflection.
JavaBeanSerializer Serialization Principle
The serialization process mainly calls the write() method of JavaBeanSerializer.
JavaBeanSerializerobtains an ObjectWriter via getObjectWriter(). Debugging this flow leads to SerializeConfig#createJavaBeanSerializer and ultimately to TypeUtils#computeGetters:
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")) {
// ...
}
}
}The method selection follows three rules:
@JSONField(serialize = false, name = "xxx") annotation
Methods starting with get Methods starting with
isSerialization 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 test produces:
isChinaName()执行!!
getEnglishName()执行!!
{"chinaName":true,"englishName":"lucy"}Code Guidelines
Serialization rules are numerous; developers must pay attention to return types, parameter counts, and annotations such as @JSONType and @JSONField. To make the codebase clearer, it is recommended to explicitly mark non‑serializable methods with @JSONField(serialize = false). The following code shows the recommended style:
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 Common Serialization Cases
The overall process follows: discover the problem → analyze the principle → solve the problem → elevate to 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.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
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.
