Migrating from Fastjson to Gson: Practical Guide, Pitfalls and Solutions
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 solutions to common issues such as date handling, SpringBoot integration, Swagger compatibility and numeric type conversion.
Why Abandon Fastjson?
Fastjson suffers from frequent high‑severity security vulnerabilities, especially related to the AutoType feature, which forced the company to repeatedly upgrade the library across all business lines.
Fastjson Replacement Options
The article focuses on Gson as the replacement, mentioning Jackson as another viable option.
Characteristics of the Three JSON Libraries
Fastjson
Fast, widely used in Alibaba’s large‑scale deployments, has extensive test cases and a simple API.
Jackson
Easy to use, no external dependencies, high performance, and produces clean, compact JSON.
Gson
Provides simple conversion between Java objects and JSON, supports immutable objects, custom serialization, complex structures, and outputs lightweight readable JSON.
Performance Comparison
Based on an internal benchmark (see GitHub repository ), the results are:
1. Serialization of a single object: Fastjson > Jackson > Gson (Gson is slower). 2. Serialization of large objects: Jackson > Fastjson > Gson. 3. Deserialization of a single object: Fastjson > Jackson > Gson (small gap). 4. Deserialization of large objects: Fastjson > Jackson > Gson (small gap).
Final Choice
Jackson is suitable for high‑performance scenarios.
Gson is preferred for high‑security scenarios.
New projects should avoid Fastjson; existing projects can either switch directly if AutoType is unused, or keep Fastjson with safe‑mode disabled, otherwise deprecate it.
Dependency Replacement Considerations
Large, multi‑team projects have complex codebases, critical online services, limited documentation, and many active branches, making the migration effort risky and costly.
Key Practices
Be extremely cautious when modifying core dependencies, as mistakes can cause severe production incidents.
Communicate with development and testing teams to plan module‑by‑module migration, schedule releases, and align on version usage.
Perform thorough regression and API testing , including JSON comparison tools for cache validation.
Assess performance impact especially for high‑traffic services.
Using Gson to Replace Fastjson
JSON Deserialization Example
String jsonCase = "[{\"id\":10001,\"date\":1609316794600,\"name\":\"XiaoMing\"},{\"id\":10002,\"date\":1609316794600,\"name\":\"XiaoLi\"}]";
// 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"));
// 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());The two libraries behave similarly, with only method name differences.
Empty Object and Array Deserialization
String jsonObjectEmptyCase = "{}";
// Fastjson
JSONObject jsonObjectEmpty = JSON.parseObject(jsonObjectEmptyCase);
System.out.println(jsonObjectEmpty);
System.out.println(jsonObjectEmpty.size());
// Gson
JsonObject jsonObjectGsonEmpty = gson.fromJson(jsonObjectEmptyCase, JsonObject.class);
System.out.println(jsonObjectGsonEmpty);
System.out.println(jsonObjectGsonEmpty.size());Both produce an empty object without errors.
String jsonArrayEmptyCase = "[]";
// Fastjson
JSONArray jsonArrayEmpty = JSON.parseArray(jsonArrayEmptyCase);
System.out.println(jsonArrayEmpty);
System.out.println(jsonArrayEmpty.size());
// Gson
JsonArray jsonArrayGsonEmpty = gson.fromJson(jsonArrayEmptyCase, JsonArray.class);
System.out.println(jsonArrayGsonEmpty);
System.out.println(jsonArrayGsonEmpty.size());Both handle empty arrays correctly.
Generic Type Handling
// Entity class
User user = new User();
user.setId(1L);
user.setUserName("Jack Ma");
// Fastjson
List
listFastjson = JSONArray.parseArray(JSON.toJSONString(userList), User.class);
List
listFastjson2 = JSON.parseObject(JSON.toJSONString(userList), new TypeReference
>(){});
// Gson
List
listGson = gson.fromJson(gson.toJson(userList), new TypeToken
>(){}.getType());Gson also supports generic deserialization.
List/Map Writing Differences
// Fastjson
JSONObject jsonObject1 = new JSONObject();
jsonObject1.put("user", user);
jsonObject1.put("userList", userList);
System.out.println(jsonObject1);
// Gson
JsonObject jsonObject = new JsonObject();
jsonObject.add("user", gson.toJsonTree(user));
System.out.println(jsonObject);Gson requires converting objects to JsonTree before insertion.
CamelCase ↔ Underscore Conversion
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
Gson gsonUnderScore = gsonBuilder.create();
System.out.println(gsonUnderScore.toJson(user));Outputs fields like {"id":1,"user_name":"Jack Ma"} .
Common Pitfalls
Date Serialization Difference
Fastjson serializes Date as Unix timestamp, while Gson uses ISO format, causing deserialization errors.
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 {
return in != null ? new Date(in.nextLong()) : null;
}
}Register the adapter when building Gson:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Date.class, new MyDateTypeAdapter())
.create();SpringBoot Exception
After switching to Gson, SpringBoot’s default Jackson mapper cannot write responses, resulting in HttpMessageNotWritableException . Fix by setting the preferred JSON mapper:
spring.mvc.converters.preferred-json-mapper=gsonSwagger Exception
Swagger fails to parse JSON when Gson is used. Add a Gson adapter for Swagger:
@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());
}
}@Mapping JsonObject as Method Parameter
Using JsonObject directly as a controller parameter causes numbers to be parsed as double (e.g., 0 becomes 0.0 ), which may break business logic.
Solution: use strongly‑typed DTOs or write a custom adapter to handle numeric types.
Conclusion
The guide is intended for teams migrating large Java codebases from Fastjson to Gson, covering security motivations, performance trade‑offs, migration strategies, and detailed solutions to typical problems such as date handling, SpringBoot and Swagger integration, and numeric type conversion.
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.