Migrating from Fastjson to Gson: Lessons Learned and Best Practices
This article documents a month‑long effort to replace Fastjson with Gson in Java projects, analyzing security vulnerabilities, comparing performance and features of Fastjson, Jackson, and Gson, and providing detailed migration steps, code examples, and pitfalls to avoid for large‑scale backend systems.
Hello everyone, I am Chen. This article summarizes more than a month of work helping our team abandon the Fastjson framework and migrate most Java repositories to Gson.
The main reason for this change is that our company is fed up with the frequent security vulnerabilities in Fastjson, which require mandatory version upgrades across the entire organization each time a flaw is discovered.
The first part briefly analyzes the pros and cons of various JSON parsing frameworks and presents several enterprise‑level migration solutions.
The second part shares the practical issues encountered when using Gson and the pitfalls discovered during the Fastjson‑to‑Gson migration.
Why abandon Fastjson?
The root cause is the high frequency of Fastjson vulnerabilities, which forces internal teams to constantly push version upgrades to prevent security issues.
Fastjson exposed many security flaws in 2020 that could bypass the autoType switch for remote code execution.
From version 1.2.59 (July 2019) to 1.2.71 (June 2020) each release added AutoType enhancements, resulting in 13 official versions with related changes.
1.2.59 released – enhanced AutoType security
1.2.60 released – added AutoType blacklist, fixed DoS issue
1.2.61 released – added AutoType security blacklist
1.2.62 released – added AutoType blacklist, improved date deserialization and JSONPath
1.2.66 released – bug fixes, security hardening, added AutoType blacklist
1.2.67 released – bug fixes, added AutoType blacklist
1.2.68 released – supported GEOJSON, added AutoType blacklist
1.2.69 released – fixed high‑risk AutoType bypass, added blacklist
1.2.70 released – improved compatibility, added blacklist
1.2.71 released – added security blacklist, no new exploitsCompared with other JSON libraries such as Gson and Jackson, Fastjson has far more vulnerabilities, especially high‑severity ones, which is the primary reason for replacement.
Fastjson replacement options
We evaluated Jackson and Gson as alternatives to Fastjson.
Characteristics of the three JSON frameworks
Fastjson
Fast – performance has never been surpassed by other Java JSON libraries since its 1.1.x release in 2011. Widely used – deployed on tens of thousands of servers at Alibaba and recognized as a popular open‑source project. Comprehensive tests – over 3,300 test cases in version 1.2.11, with regression testing on each release. Simple API – concise and easy to use.
Jackson
Easy to use – high‑level API simplifies common use cases. No need to create mappings – default mappings handle most object serialization. High performance – fast and low memory consumption, suitable for large object graphs. Clean JSON – produces compact, readable JSON. Zero dependencies – only requires the JDK.
Gson
Provides a simple mechanism to convert Java objects to JSON and vice‑versa, similar to using toString() and constructors. Supports immutable objects. Allows custom object representations. Handles arbitrarily complex objects. Outputs lightweight, readable JSON.
Performance comparison
Source code for the benchmark: https://github.com/zysrxx/json-comparison
1. Serialization (single object): Fastjson > Jackson > Gson (Fastjson and Jackson are close, Gson is slower). 2. Serialization (large object): Jackson > Fastjson > Gson (Jackson shows clear advantage). 3. Deserialization (single object): Fastjson > Jackson > Gson (differences are small). 4. Deserialization (large object): Fastjson > Jackson > Gson (differences are minimal).
Final selection
Jackson is suitable for high‑performance scenarios; Gson is suitable for high‑security scenarios.
New projects should stop using Fastjson. For legacy systems, consider the migration cost and choose one of the following:
If the project does not use autoType, switch directly to a non‑Fastjson library; if the cost is high, keep Fastjson but disable safe mode.
If autoType is used, actively deprecate Fastjson.
Considerations when replacing dependencies
Large enterprise projects have complex codebases, multiple maintainers, and critical online services, making Fastjson‑to‑Gson migration a painful process.
Key points to focus on during dependency replacement:
Be extremely cautious
Any mistake in a critical business project can cause severe incidents. Even if the functional surface does not change, the underlying code modifications can be risky.
Communicate with development and testing teams
Plan the migration in modules, define clear schedules, and ensure both developers and testers are aware of version changes and API differences.
Perform thorough regression and API testing
Allocate sufficient testing effort for full regression and, if possible, interface comparison testing (e.g., using a JSON diff tool).
Consider performance differences
Framework swaps can affect latency in high‑traffic services; evaluate the impact before rollout.
Using Gson to replace Fastjson
Below are two common JSON handling methods with detailed code examples to help you switch to Gson seamlessly.
JSON deserialization
String jsonCase = "[{\"id\":10001,\"date\":1609316794600,\"name\":\"小明\"},{\"id\":10002,\"date\":1609316794600,\"name\":\"小李\"}]";
// Fastjson
JSONArray jsonArray = JSON.parseArray(jsonCase);
System.out.println(jsonArray);
System.out.println(jsonArray.getJSONObject(0).getString("name"));
System.out.println(jsonArray.getJSONObject(1).getString("name"));
// Output:
// [{"date":1609316794600,"name":"小明","id":10001},{"date":1609316794600,"name":"小李","id":10002}]
// 小明
// 小李
// Gson
JsonArray jsonArrayGson = gson.fromJson(jsonCase, JsonArray.class);
System.out.println(jsonArrayGson);
System.out.println(jsonArrayGson.get(0).getAsJsonObject().get("name").getAsString());
System.out.println(jsonArrayGson.get(1).getAsJsonObject().get("name").getAsString());
// Output:
// [{"id":10001,"date":1609316794600,"name":"小明"},{"id":10002,"date":1609316794600,"name":"小李"}]
// 小明
// 小李Both libraries handle empty objects and arrays without exceptions:
// Empty object
String jsonObjectEmptyCase = "{}";
// Fastjson
JSONObject jsonObjectEmpty = JSON.parseObject(jsonObjectEmptyCase);
System.out.println(jsonObjectEmpty); // {}
System.out.println(jsonObjectEmpty.size()); // 0
// Gson
JsonObject jsonObjectGsonEmpty = gson.fromJson(jsonObjectEmptyCase, JsonObject.class);
System.out.println(jsonObjectGsonEmpty); // {}
System.out.println(jsonObjectGsonEmpty.size()); // 0
// Empty array
String jsonArrayEmptyCase = "[]";
// Fastjson
JSONArray jsonArrayEmpty = JSON.parseArray(jsonArrayEmptyCase);
System.out.println(jsonArrayEmpty); // []
System.out.println(jsonArrayEmpty.size()); // 0
// Gson
JsonArray jsonArrayGsonEmpty = gson.fromJson(jsonArrayEmptyCase, JsonArray.class);
System.out.println(jsonArrayGsonEmpty); // []
System.out.println(jsonArrayGsonEmpty.size()); // 0Generic type handling
// Entity class
User user = new User();
user.setId(1L);
user.setUserName("马云");
// Fastjson
List
userListResultFastjson = JSONArray.parseArray(JSON.toJSONString(userList), User.class);
List
userListResultFastjson2 = JSON.parseObject(JSON.toJSONString(userList), new TypeReference
>(){});
System.out.println(userListResultFastjson);
System.out.println("userListResultFastjson2" + userListResultFastjson2);
// Gson
List
userListResultTrue = gson.fromJson(gson.toJson(userList), new TypeToken
>(){}.getType());
System.out.println("userListResultGson" + userListResultGson);Gson also supports generics.
List/Map writing
// Fastjson
JSONObject jsonObject1 = new JSONObject();
jsonObject1.put("user", user);
jsonObject1.put("userList", userList);
System.out.println(jsonObject1); // {"userList":[{"id":1,"userName":"马云"},null],"user":{"id":1,"userName":"马云"}}
// Gson
JsonObject jsonObject = new JsonObject();
jsonObject.add("user", gson.toJsonTree(user));
System.out.println(jsonObject); // {"user":{"id":1,"userName":"马云"},"userList":[{"id":1,"userName":"马云"},null]}Gson requires converting objects to JsonTree before insertion.
CamelCase ↔ snake_case conversion
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
Gson gsonUnderScore = gsonBuilder.create();
System.out.println(gsonUnderScore.toJson(user)); // {"id":1,"user_name":"马云"}Common pitfalls and solutions
Date serialization differences
Fastjson serializes Date as a Unix timestamp, while Gson uses the standard ISO format, causing deserialization errors when switching.
public class MyDateTypeAdapter extends TypeAdapter
{
@Override
public void write(JsonWriter out, Date value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(value.getTime());
}
}
@Override
public Date read(JsonReader in) throws IOException {
if (in != null) {
return new Date(in.nextLong());
} else {
return null;
}
}
}
Gson gson = new GsonBuilder()
.registerTypeAdapter(Date.class, new MyDateTypeAdapter())
.create();This adapter makes Gson handle Date as Unix timestamps, compatible with existing Fastjson‑cached data.
SpringBoot mapper conflict
SpringBoot defaults to Jackson; after switching to Gson, HTTP message conversion fails.
## Preferred JSON mapper for HTTP message conversion
spring.mvc.converters.preferred-json-mapper=gsonSwagger incompatibility
Introducing Gson can break Swagger JSON generation. Adding a custom Gson converter resolves the issue.
@Configuration
public class GsonSwaggerConfig {
@Bean
public IGsonHttpMessageConverter IGsonHttpMessageConverter() {
return new IGsonHttpMessageConverter();
}
}
public class IGsonHttpMessageConverter extends GsonHttpMessageConverter {
public IGsonHttpMessageConverter() {
super.setGson(new GsonBuilder()
.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter())
.serializeNulls()
.create());
}
}
public class SpringfoxJsonToGsonAdapter implements JsonSerializer
{
@Override
public JsonElement serialize(Json json, Type type, JsonSerializationContext ctx) {
return new JsonParser().parse(json.value());
}
}Using JsonObject as a request parameter
Gson converts numeric fields to double by default, which can cause type mismatches.
public ResponseResult
submitAudit(@RequestBody JsonObject jsonObject) { ... }Solution: use a concrete POJO for request bodies or implement a custom adapter to handle numbers precisely.
Conclusion
This article is intended for developers who need to migrate their projects from Fastjson to Gson. While small personal projects may not require such effort, the article outlines general migration considerations such as framework compatibility, performance impact, and required engineering effort.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
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.