Why a Single Log Line Triggered a FastJSON NullPointerException – Deep Debugging Guide

The article walks through a real‑world FastJSON serialization failure caused by a newly added log statement, explains why FastJSON invokes certain getter methods during serialization, shows the ASM‑generated serializer internals, and proposes annotation‑based coding standards to avoid similar bugs.

Architect
Architect
Architect
Why a Single Log Line Triggered a FastJSON NullPointerException – Deep Debugging Guide

Incident Review

A simple log line was added to the review code before a nightly deployment. Although the change seemed harmless, the service immediately generated numerous alerts, prompting an urgent rollback and removal of the logging code.

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 that serializes the DTO:
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 the country field is still null.

Why does serialization invoke isChinaName()?

What other methods are executed during the serialization process?

Source Code Analysis

Debugging the call stack reveals that FastJSON generates a class named ASMSerializer_1_CountryDTO using the ASM library. The generated class’s write method is invoked by FastJSON’s serializer.

ASM is used to create a serializer class at runtime, replacing Java reflection and thus avoiding the overhead of repeated reflective calls.

JavaBeanSerializer Serialization Principle

During serialization FastJSON ultimately calls JavaBeanSerializer.write. This method obtains an ObjectWriter via getObjectWriter(), which leads to the creation of a JavaBeanSerializer inside SerializeConfig#createJavaBeanSerializer. The critical method in this chain is com.alibaba.fastjson.util.TypeUtils#computeGetters, which determines which getters are included.

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) {
        // ... omitted code ...
        if (method.getReturnType().equals(Void.TYPE)) {
            continue;
        }
        if (method.getParameterTypes().length != 0) {
            continue;
        }
        JSONField annotation = TypeUtils.getAnnotation(method, JSONField.class);
        if (annotation != null) {
            if (!annotation.serialize()) {
                continue;
            }
            if (annotation.name().length() != 0) {
                // ... omitted code ...
            }
        }
        if (methodName.startsWith("get")) {
            // ... omitted code ...
        }
        if (methodName.startsWith("is")) {
            // ... omitted code ...
        }
    }
}

The analysis shows three categories of methods that FastJSON may invoke: @JSONField(serialize = false, name = "xxx") annotated methods are excluded.

Methods whose names start with get (standard getters).

Methods whose names start with is (boolean‑style getters).

Code Standards

To make the serialization intent explicit and avoid accidental method execution, the article recommends using @JSONField(serialize = false) on methods that should not participate in serialization.

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

Common Serialization Scenarios

The following diagram summarizes the typical flow of a FastJSON serialization issue, from problem discovery to principle analysis, solution implementation, and finally coding standards.

In summary, the root cause was that FastJSON, via its ASM‑generated serializer, automatically called the isChinaName() getter during serialization. By explicitly marking non‑serializable methods with @JSONField(serialize = false), developers can prevent similar null‑pointer crashes and make the serialization contract clear.

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.

DebuggingJavaserializationannotationsfastjsonASM
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

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.