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.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Why Does FastJSON Invoke isChinaName() During Serialization? Explore the Mechanics

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

is

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

fastjsonASMJava serializationJSONFieldJavaBeanSerializer
Java Backend Technology
Written by

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!

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.