Why Does FastJSON Invoke isChinaName()? Uncovering Hidden Serialization Pitfalls

A seemingly harmless log addition triggered a cascade of FastJSON serialization calls, leading to a NullPointerException caused by the isChinaName() method, and the article walks through the debugging process, explains the underlying serialization mechanics, and proposes a clean solution using @JSONField(serialize=false).

Java Interview Crash Guide
Java Interview Crash Guide
Java Interview Crash Guide
Why Does FastJSON Invoke isChinaName()? Uncovering Hidden Serialization Pitfalls

Recently a simple log line was added before a nightly release, but after deployment a flood of alerts forced an immediate rollback. The root cause was a NullPointerException during FastJSON serialization, where the method isChinaName() was invoked on a null country field.

Scenario Reconstruction

Defined a CountryDTO class with a String country field, standard getter/setter, and an isChinaName() method that compares country to "中国".
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("中国"); }
}
Test class FastJonTest creates a CountryDTO , serializes it with JSON.toJSONString , and prints the result.
public class FastJonTest {
    @Test
    public void testSerialize() {
        CountryDTO countryDTO = new CountryDTO();
        String str = JSON.toJSONString(countryDTO);
        System.out.println(str);
    }
}

The runtime error shows that during serialization isChinaName() was executed, but this.country was null, causing the NPE.

Source Code Analysis

Debugging revealed that FastJSON generates a dynamic class ASMSerializer_1_CountryDTO via ASM to replace reflection. The write() method of JavaBeanSerializer is the entry point.

Inside JavaBeanSerializer, getObjectWriter() eventually calls SerializeConfig#createJavaBeanSerializer, which uses TypeUtils.computeGetters to collect getter methods.

public static List<FieldInfo> computeGetters(Class<?> clazz, ... ) {
    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;
        String methodName = method.getName();
        if (methodName.startsWith("get")) { /* treat as getter */ }
        if (methodName.startsWith("is")) { /* treat as boolean getter */ }
    }
}

The algorithm classifies methods into three cases that affect serialization:

@JSONField(serialize = false, name = "xxx") annotation

Methods starting with get Methods starting with is Because isChinaName() matches the is pattern and returns a Boolean, FastJSON includes it in the serialization process, leading to the NPE.

JavaBeanSerializer Serialization Principle

The serializer ultimately calls JavaBeanSerializer.write(), which iterates over the collected getters and writes their values to the JSON output.

Serialization Flowchart

Example Code

Four representative cases are demonstrated:

/**
 * 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() 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 void getEnglishName3() { System.out.println("getEnglishName3() executed!!"); }
    @JSONField(serialize = false)
    public String isChinaName2() { System.out.println("isChinaName2() executed!!"); return "isChinaName2"; }
}

Running the test prints:

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

Code Guidelines

To avoid ambiguous serialization rules, explicitly mark methods that should not participate using @JSONField(serialize = false). This makes the intent clear and prevents accidental NPEs.

High‑Frequency Serialization Scenarios

The article follows a logical flow: problem discovery → principle analysis → solution implementation → coding standards, illustrating how a single issue can lead to broader best‑practice recommendations.

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.

DebuggingFastJSONNullPointerExceptionJava serializationJSONFieldjava-bean
Java Interview Crash Guide
Written by

Java Interview Crash Guide

Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.

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.