Unveiling FastJSON 2.0.31: A Deep Dive into Its Serialization Mechanics
This article provides a comprehensive walkthrough of FastJSON 2.0.31's serialization and deserialization process, covering its core architecture, step‑by‑step execution flow, common pitfalls such as getter misuse and circular references, and practical guidance on annotations and SerializerFeature options.
1. Introduction
In everyday development FastJSON is frequently used for JSON serialization and deserialization. While it offers great convenience, its internal mechanisms are often overlooked, leading to numerous issues such as unexpected getter execution, Full GC spikes, and bugs in mini‑programs.
FastJSON serialization pitfalls – getter methods being invoked unintentionally.
Improper FastJSON usage causing online Full GC problems.
FastJSON‑induced Full GC investigations.
FastJSON‑related mini‑program bug investigations.
Bug tracing caused by JSON.toJSONString.
Two common problems arise when using FastJSON without understanding its internals: serialization failures and deserialization anomalies.
2. FastJSON Source Code Exploration
2.1 Overall Structure
The source tree shows that the entry class is JSON, which groups its methods into four categories:
Serialization methods: JSON.toJSONString() (returns a String) and JSON.toJSONBytes() (returns a byte array).
Deserialization methods: JSON.parseObject(), JSON.parse(), and JSON.parseArray().
Object conversion: JSON.toJavaObject().
Writing JSON to streams: JSON.writeJSONString().
Key packages include annotation, asm, parser, and serializer. annotation: defines annotations such as @JSONField and @JSONType that control serialization behavior. asm: uses the ASM library to generate optimized bytecode at runtime, improving performance. parser: contains deserialization classes, all extending ObjectDeserializer. serializer: contains serialization classes, all extending ObjectSerializer.
2.2 Detailed Serialization Flow
When JSON.toJSONString(obj) is called, the following steps occur:
Create a SerializeWriter (similar to a StringBuilder) to hold intermediate output.
Instantiate a JSONSerializer which holds references to all concrete serializers.
Invoke the serializer’s write() method to start the actual serialization.
During this process, SerializeConfig maintains an IdentityHashMap that maps Java classes to their corresponding serializers. If a serializer is missing, it is created on‑the‑fly.
If the serializer is null, a JavaBeanSerializer is built, which in turn creates a SerializeBeanInfo describing fields and methods. The asm flag may trigger dynamic generation of an ASM‑based serializer for better performance.
After the serializer is ready, the following actions happen:
The serializer writes data into the SerializeWriter.
The writer finally converts its buffer into the resulting JSON string.
Below is the core overload that performs the actual work:
public static String toJSONString(Object object, SerializeConfig config, SerializeFilter[] filters, String dateFormat, int defaultFeatures, SerializerFeature... features) { SerializeWriter out = new SerializeWriter(null, defaultFeatures, features); try { JSONSerializer serializer = new JSONSerializer(out, config); if (dateFormat != null && dateFormat.length() != 0) { serializer.setDateFormat(dateFormat); serializer.config(SerializerFeature.WriteDateUseDateFormat, true); } if (filters != null) { for (SerializeFilter filter : filters) { serializer.addFilter(filter); } } serializer.write(object); return out.toString(); } finally { out.close(); }}The serializer’s write(Object object) method resolves the appropriate ObjectSerializer for the object’s class and delegates the actual field processing.
public final void write(Object object) { if (object == null) { out.writeNull(); return; } Class<?> clazz = object.getClass(); ObjectSerializer writer = getObjectWriter(clazz); try { writer.write(this, object, null, null, 0); } catch (IOException e) { throw new JSONException(e.getMessage(), e); }}The getObjectWriter method looks up an existing serializer in the map or creates a new JavaBeanSerializer when necessary.
public ObjectSerializer getObjectWriter(Class<?> clazz, boolean create) { ObjectSerializer writer = get(clazz); if (writer != null) { return writer; } if (writer == null && create) { writer = createJavaBeanSerializer(clazz); put(clazz, writer); } return writer;}During bean serialization, SerializeConfig may generate an ASM serializer via createASMSerializer, which produces bytecode that directly writes fields without reflection.
2.3 Serialization Caveats
Getter Method Normalization
Only properties with a corresponding getter are serialized.
Getter logic should be side‑effect‑free; complex operations can cause unexpected failures.
Avoid defining non‑field getters that start with get unless you explicitly disable them with @JSONField(serialize = false) or the SerializerFeature.IgnoreNonFieldGetter flag.
Example: a class with a getNoField() method that throws an exception will cause serialization to fail unless the ignore flag is set.
class MyTest { private String name; public int getNoField() { return 1/0; } }public class FastJsonTest { public static void main(String[] args) { MyTest myTest = new MyTest(); myTest.setName("Zhang San"); System.out.println(JSON.toJSONString(myTest)); }}Running the above throws ArithmeticException. Adding SerializerFeature.IgnoreNonFieldGetter resolves the issue.
Boolean Property Naming
Boolean fields should not be prefixed with is because FastJSON strips the prefix and may misinterpret the property name.
@Data class MyTest { private boolean isActive; private boolean value; public int getNoField() { return 1; } }Serializing this class yields {"active":true,"noField":1,"value":false}, which is incorrect.
Duplicate/ Circular References
FastJSON replaces repeated references with $ref placeholders to save space and avoid infinite loops.
User user1 = new User(); user1.setAge(18); user1.setName("Zhang San"); List<User> list = new ArrayList<>(); list.add(user1); list.add(user1); System.out.println(JSON.toJSONString(list));Output: [{"age":18,"name":"Zhang San"},{"$ref":"$[0]"}]. Use SerializerFeature.DisableCircularReferenceDetect to disable this behavior.
3. Annotations and SerializerFeature Options
Commonly used annotations: @JSONField: controls name, order, format, inclusion, and serialization features for a field or method. @JSONType: applies to a class to configure inclusion, order, and features for all its fields. @JSONCreator: marks a constructor or factory method for deserialization. @JSONPOJOBuilder: defines a builder class and method for constructing complex objects.
Key SerializerFeature constants (highlighted in yellow in the original source) include: QuoteFieldNames – output field names with quotes. UseSingleQuotes – use single quotes instead of double quotes. WriteMapNullValue – serialize fields with null values. WriteEnumUsingToString – serialize enums using toString(). PrettyFormat – format JSON with indentation. WriteClassName – include the class name in the output. DisableCircularReferenceDetect – turn off circular reference detection. DisableASM – disable ASM‑based serializer generation.
These options allow fine‑grained control over the resulting JSON.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Alibaba Cloud Developer
Alibaba's official tech channel, featuring all of its technology innovations.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
