Why Does FastJSON Invoke isChinaName() During Serialization? Explore the Mechanics
This article analyzes a runtime NullPointerException caused by FastJSON serialization invoking the isChinaName() method, explains the underlying ASM-generated serializer, details how JavaBeanSerializer determines which getters and is‑methods are serialized, and proposes using @JSONField(serialize = false) to control the process.
Online Incident Review
Recently a simple feature was added with an extra log line reflecting the company's values. After deployment the service triggered a flood of alerts, prompting an immediate rollback and removal of the log statement.
Scenario Reconstruction
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 FastJSON calls isChinaName() during serialization while country is null.
Root Cause Questions
Why does serialization invoke isChinaName()?
What methods are executed during FastJSON serialization?
Source Code Analysis
Debugging reveals the call chain reaches ASMSerializer_1_CountryDTO.write, a class dynamically generated by FastJSON using the ASM bytecode framework.
The core of FastJSON serialization is the JavaBeanSerializer class, whose write() method is invoked. It obtains an object writer via SerializeConfig#createJavaBeanSerializer, which eventually calls TypeUtils#computeGetters to decide which getters and isXxx methods participate.
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) {
// skip void return or methods with parameters
// process @JSONField annotation
// handle methods starting with "get" or "is"
}
}The method selects three kinds of members:
@JSONField(serialize = false, name = "xxx") annotations
Methods starting with get Methods starting with
isSerialization Flowchart
Example Code
/** case1: @JSONField(serialize = false) */
@JSONField(serialize = false)
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"; }Running the test produces:
isChinaName() executed!!
getEnglishName() executed!!
{"chinaName":true,"englishName":"lucy"}Code Guidelines
To avoid accidental method invocation during serialization, explicitly mark methods with @JSONField(serialize = false). The revised CountryDTO looks like:
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!!"); }
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 String getEnglishName2() { System.out.println("getEnglishName2() 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"; }
}Three Frequently Encountered Serialization Cases
The overall workflow follows: discover the problem → analyze the principle → solve the issue → formalize coding standards.
Source: 老鹰汤, juejin.cn/post/7156439842958606349
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 Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
