Backend Development 8 min read

Understanding FastJSON Serialization: Why isChinaName() Is Invoked and How to Control Serialized Methods

This article analyzes a FastJSON serialization issue where a getter method is unexpectedly called, explains the underlying ASM‑generated serializer mechanism, details which methods FastJSON invokes during serialization, and proposes using @JSONField(serialize = false) to explicitly exclude methods from the serialization process.

Architecture Digest
Architecture Digest
Architecture Digest
Understanding FastJSON Serialization: Why isChinaName() Is Invoked and How to Control Serialized Methods

Incident Review

Recently a simple logging line was added to a new feature, but after deployment a cascade of alerts forced a rollback. The root cause was a NullPointerException triggered during JSON serialization.

Reproducing the Problem

Define a CountryDTO class and a test class FastJonTest that serializes 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);
    }
}

Running the test throws a NullPointerException because isChinaName() is invoked while country is still null.

Why isChinaName() Is Executed?

FastJSON uses ASM to generate a serializer class (e.g., ASMSerializer_1_CountryDTO ) that replaces reflection. The generated write() method calls JavaBeanSerializer.write() , which obtains an ObjectWriter that inspects getter methods.

During the inspection FastJSON’s computeGetters method treats any public method with no parameters and a non‑void return type that starts with get or is as a property accessor, unless it is excluded by annotations.

public static List
computeGetters(Class
clazz, JSONType jsonType, ... ) {
    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;
        if (methodName.startsWith("get")) { /* handle getter */ }
        if (methodName.startsWith("is")) { /* handle boolean getter */ }
    }
}

Thus isChinaName() is considered a boolean getter and is called during serialization, leading to the NPE.

What Methods Are Invoked During Serialization?

@JSONField(serialize = false, name = "xxx") – excluded.

Methods starting with get – treated as getters.

Methods starting with is – treated as boolean getters.

Recommended Coding Practice

To avoid accidental invocation, explicitly mark methods that should not be serialized with @JSONField(serialize = false) . The following example shows a clean version of CountryDTO where only intended properties are serialized:

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"; }
}

Running the test now produces the expected JSON without NPE:

isChinaName() executed!!
getEnglishName() executed!!
{"chinaName":true,"englishName":"lucy"}

Conclusion

The debugging process followed the pattern: discover the issue → analyze the underlying FastJSON serialization mechanism → apply a solution (explicit exclusion) → formalize a coding guideline. Understanding which methods FastJSON treats as properties helps prevent similar bugs in backend Java projects.

JavaBackend DevelopmentSerializationfastjsonASMJSONField
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.