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<FieldInfo> 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentserializationASMJSONField
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

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.