跳到主要内容Java 处理 JSON 的实战技巧与性能优化 | 极客日志Javajava
Java 处理 JSON 的实战技巧与性能优化
Java 开发中 JSON 处理涉及 Gson、Jackson 和 Fastjson 三大框架。Gson 简洁易用,Jackson 功能强大且是 Spring 默认集成,Fastjson 性能高但需警惕安全漏洞。实战中应注重对象复用、流式处理及异常管理,统一配置日期格式与序列化规则能有效提升代码质量。针对字段映射、null 值处理、循环引用及复杂嵌套结构,各框架均有对应的注解或 API 解决方案。安全方面,务必禁用自动类型加载并限制反序列化范围,同时利用工具类封装简化调用流程。
DebugKing5 浏览 前言
JSON(JavaScript Object Notation)作为轻量级的数据交换格式,在 Java 开发中几乎是无处不在。无论是接口交互、配置文件还是日志存储,JSON 处理能力都是后端工程师的基本功。本文将结合 Gson、Jackson 和 Fastjson 三大主流框架,分享一些实战中的使用技巧、坑点规避以及性能优化方案。
JSON 处理框架对比
Java 生态中有多个优秀的 JSON 处理库,它们各有千秋。Gson 以简洁著称,Jackson 功能强大且是 Spring 默认集成,Fastjson 则以高性能闻名但需注意安全版本。选择时通常取决于项目技术栈和对性能的具体要求。
Gson 使用技巧
基础用法
Gson 由 Google 开发,核心思想是将 Java 对象转换为 JSON 字符串,反之亦然。
Maven 依赖
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
基本序列化与反序列化
Gson gson = new Gson();
User user = new User("张三", 25);
String json = gson.toJson(user);
System.out.println(json);
String jsonString = "{\"name\":\"李四\",\"age\":30}";
User gson.fromJson(jsonString, User.class);
System.out.println(parsedUser.getName());
parsedUser
=
集合类型的序列化与反序列化
处理泛型集合时,Gson 需要借助 TypeToken 来保留类型信息。
List<User> userList = Arrays.asList(
new User("张三", 25),
new User("李四", 30)
);
String jsonList = gson.toJson(userList);
Type userListType = new TypeToken<List<User>>(){}.getType();
List<User> parsedList = gson.fromJson(jsonList, userListType);
User[] userArray = gson.fromJson(jsonList, User[].class);
List<User> userList2 = Arrays.asList(userArray);
高级特性
GsonBuilder 配置
通过 GsonBuilder 可以灵活定制序列化行为,比如格式化输出、处理 null 值或日期格式。
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.serializeNulls()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.excludeFieldsWithoutExposeAnnotation()
.registerTypeAdapter(Date.class, new DateSerializer())
.disableHtmlEscaping()
.create();
自定义序列化和反序列化
当字段名映射复杂或需要特殊转换逻辑时,实现 JsonSerializer 和 JsonDeserializer 接口。
public class UserSerializer implements JsonSerializer<User> {
@Override
public JsonElement serialize(User user, Type type, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("userName", user.getName());
jsonObject.addProperty("userAge", user.getAge());
return jsonObject;
}
}
public class UserDeserializer implements JsonDeserializer<User> {
@Override
public User deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String name = jsonObject.get("userName").getAsString();
int age = jsonObject.get("userAge").getAsInt();
return new User(name, age);
}
}
Gson gson = new GsonBuilder()
.registerTypeAdapter(User.class, new UserSerializer())
.registerTypeAdapter(User.class, new UserDeserializer())
.create();
使用注解控制序列化
利用注解可以更优雅地控制字段行为,如重命名、忽略或暴露控制。
public class User {
@SerializedName("user_name")
private String name;
@SerializedName(value = "user_age", alternate = {"age", "userAge"})
private int age;
@Expose(serialize = true, deserialize = false)
private String password;
@Expose(serialize = false, deserialize = true)
private String email;
}
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
处理复杂嵌套对象
对于结构不确定的 JSON,可以使用 JsonElement 进行解析。
String complexJson = "{\"name\":\"张三\",\"address\":{\"city\":\"北京\",\"district\":\"朝阳区\"}}";
JsonElement jsonElement = JsonParser.parseString(complexJson);
JsonObject jsonObject = jsonElement.getAsJsonObject();
String name = jsonObject.get("name").getAsString();
JsonObject addressObject = jsonObject.getAsJsonObject("address");
String city = addressObject.get("city").getAsString();
if (jsonObject.has("phone")) {
String phone = jsonObject.get("phone").getAsString();
}
注:Gson 2.8.6+ 版本中,JsonParser 的静态方法已被弃用,建议使用以下方式:
Gson gson = new Gson();
JsonReader reader = new JsonReader(new StringReader(jsonString));
reader.setLenient(true);
JsonElement jsonElement = gson.fromJson(reader, JsonElement.class);
Jackson 使用技巧
基础用法
Jackson 是 Java 生态中最流行的 JSON 库之一,Spring Boot 默认集成。它功能丰富,性能优异。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.13.0</version>
</dependency>
ObjectMapper objectMapper = new ObjectMapper();
User user = new User("张三", 25);
String json = objectMapper.writeValueAsString(user);
System.out.println(json);
String jsonString = "{\"name\":\"李四\",\"age\":30}";
User parsedUser = objectMapper.readValue(jsonString, User.class);
System.out.println(parsedUser.getName());
String prettyJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);
集合类型的序列化与反序列化
Jackson 推荐使用 TypeReference 来处理泛型集合的反序列化。
List<User> userList = Arrays.asList(
new User("张三", 25),
new User("李四", 30)
);
String jsonList = objectMapper.writeValueAsString(userList);
List<User> parsedList = objectMapper.readValue(
jsonList,
new TypeReference<List<User>>() {}
);
Map<String, Object> map = objectMapper.readValue(
jsonString,
new TypeReference<Map<String, Object>>() {}
);
高级特性
ObjectMapper 配置
ObjectMapper 的配置项非常丰富,可以根据业务需求调整行为。
ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(SerializationFeature.INDENT_OUTPUT, true)
.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false)
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
.registerModule(new JavaTimeModule());
自定义序列化和反序列化
通过扩展 JsonSerializer 和 JsonDeserializer 实现自定义逻辑。
public class UserSerializer extends JsonSerializer<User> {
@Override
public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("userName", user.getName());
jsonGenerator.writeNumberField("userAge", user.getAge());
jsonGenerator.writeEndObject();
}
}
public class UserDeserializer extends JsonDeserializer<User> {
@Override
public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
String name = node.get("userName").asText();
int age = node.get("userAge").asInt();
return new User(name, age);
}
}
SimpleModule module = new SimpleModule();
module.addSerializer(User.class, new UserSerializer());
module.addDeserializer(User.class, new UserDeserializer());
objectMapper.registerModule(module);
使用注解控制序列化
Jackson 提供了丰富的注解来控制序列化细节。
public class User {
@JsonProperty("user_name")
private String name;
@JsonIgnore
private String password;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@JsonInclude(JsonInclude.Include.NON_NULL)
private String email;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<String> hobbies;
}
处理复杂 JSON 结构
Jackson 的 JsonNode 树模型非常适合处理动态或复杂的 JSON 结构。
String complexJson = "{\"name\":\"张三\",\"address\":{\"city\":\"北京\",\"district\":\"朝阳区\"}}";
JsonNode rootNode = objectMapper.readTree(complexJson);
String name = rootNode.get("name").asText();
String city = rootNode.get("address").get("city").asText();
if (rootNode.has("phone")) {
String phone = rootNode.get("phone").asText();
}
JsonNode arrayNode = rootNode.get("contacts");
if (arrayNode != null && arrayNode.isArray()) {
for (JsonNode node : arrayNode) {
String contactName = node.get("name").asText();
}
}
使用树模型
除了读取,还可以直接构建 JSON 树。
ObjectMapper mapper = new ObjectMapper();
ObjectNode rootNode = mapper.createObjectNode();
rootNode.put("name", "张三");
rootNode.put("age", 25);
ObjectNode addressNode = mapper.createObjectNode();
addressNode.put("city", "北京");
addressNode.put("district", "朝阳区");
rootNode.set("address", addressNode);
ArrayNode hobbiesNode = mapper.createArrayNode();
hobbiesNode.add("读书");
hobbiesNode.add("游泳");
rootNode.set("hobbies", hobbiesNode);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode);
Fastjson 使用技巧
基础用法
Fastjson 是阿里巴巴开源的高性能 JSON 库,API 设计非常简洁。
安全提示: Fastjson 历史上曾存在严重的安全漏洞,使用时请务必更新到最新版本,并严格遵循官方安全建议。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
User user = new User("张三", 25);
String json = JSON.toJSONString(user);
System.out.println(json);
String jsonString = "{\"name\":\"李四\",\"age\":30}";
User parsedUser = JSON.parseObject(jsonString, User.class);
System.out.println(parsedUser.getName());
String prettyJson = JSON.toJSONString(user, SerializerFeature.PrettyFormat);
List<User> userList = Arrays.asList(
new User("张三", 25),
new User("李四", 30)
);
String jsonList = JSON.toJSONString(userList);
List<User> parsedList = JSON.parseArray(jsonList, User.class);
JSONObject jsonObject = JSON.parseObject(jsonString);
String name = jsonObject.getString("name");
int age = jsonObject.getInteger("age");
高级特性
SerializerFeature 配置
通过序列化特性控制输出格式和行为。
String json = JSON.toJSONString(user,
SerializerFeature.PrettyFormat,
SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.WriteNullNumberAsZero,
SerializerFeature.WriteNullBooleanAsFalse,
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.WriteDateUseDateFormat,
SerializerFeature.WriteMapNullValue
);
public class UserSerializer implements ObjectSerializer {
@Override
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
User user = (User) object;
SerializeWriter out = serializer.out;
out.write('{');
out.writeFieldName("userName");
serializer.write(user.getName());
out.write(',');
out.writeFieldName("userAge");
serializer.write(user.getAge());
out.write('}');
}
}
public class UserDeserializer implements ObjectDeserializer {
@Override
public User deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
JSONObject jsonObject = parser.parseObject();
String name = jsonObject.getString("userName");
int age = jsonObject.getInteger("userAge");
return new User(name, age);
}
@Override
public int getFastMatchToken() {
return 0;
}
}
ParserConfig.getGlobalInstance().putDeserializer(User.class, new UserDeserializer());
SerializeConfig.getGlobalInstance().put(User.class, new UserSerializer());
public class User {
@JSONField(name = "user_name")
private String name;
@JSONField(serialize = false)
private String password;
@JSONField(deserialize = false)
private String token;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@JSONField(ordinal = 1)
private String email;
}
使用 JSONObject 和 JSONArray
适合处理动态结构或临时组装数据。
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "张三");
jsonObject.put("age", 25);
JSONObject addressObject = new JSONObject();
addressObject.put("city", "北京");
addressObject.put("district", "朝阳区");
jsonObject.put("address", addressObject);
JSONArray hobbiesArray = new JSONArray();
hobbiesArray.add("读书");
hobbiesArray.add("游泳");
jsonObject.put("hobbies", hobbiesArray);
String json = jsonObject.toJSONString();
JSONObject parsedObject = JSON.parseObject(complexJson);
String city = parsedObject.getJSONObject("address").getString("city");
JSONArray contacts = parsedObject.getJSONArray("contacts");
for (int i = 0; i < contacts.size(); i++) {
String contactName = contacts.getJSONObject(i).getString("name");
}
安全配置
这是使用 Fastjson 最关键的部分,务必防止反序列化漏洞。
ParserConfig.getGlobalInstance().setAutoTypeSupport(false);
ParserConfig parserConfig = ParserConfig.getGlobalInstance();
parserConfig.addAccept("com.example.model.");
JSONReader jsonReader = new JSONReader(new StringReader(jsonString));
jsonReader.config(Feature.SupportAutoType, false);
性能优化技巧
通用优化原则
- 重用对象:避免重复创建 JSON 解析器实例(Gson、ObjectMapper 或 JSON),这些实例通常是线程安全的。
- 避免不必要的转换:直接在流上进行操作,减少中间字符串的创建。
- 选择合适的解析模式:根据需求选择树模型或流式 API。
- 优化数据结构:减少不必要的嵌套和冗余字段。
- 合理使用缓存:缓存序列化/反序列化的结果。
Gson 性能优化
public class GsonHolder {
private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
.create();
public static Gson getGson() {
return GSON;
}
}
TypeAdapter<User> userAdapter = GsonHolder.getGson().getAdapter(User.class);
userAdapter.write(new JsonWriter(new FileWriter("user.json")), user);
User user = userAdapter.read(new JsonReader(new FileReader("user.json")));
String json = GsonHolder.getGson().toJson(user);
Jackson 性能优化
public class ObjectMapperHolder {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
public static ObjectMapper getObjectMapper() {
return OBJECT_MAPPER;
}
}
ObjectMapperHolder.getObjectMapper().writeValue(new File("large.json"), largeObject);
LargeObject largeObject = ObjectMapperHolder.getObjectMapper()
.readValue(new File("large.json"), LargeObject.class);
JsonNode rootNode = ObjectMapperHolder.getObjectMapper().readTree(jsonString);
String neededField = rootNode.get("neededField").asText();
JavaType listType = ObjectMapperHolder.getObjectMapper().getTypeFactory()
.constructCollectionType(List.class, User.class);
Fastjson 性能优化
List<User> userList = JSON.parseObject(jsonList, new TypeReference<List<User>>() {});
byte[] bytes = JSONB.toJSONBBytes(user);
User parsedUser = JSONB.parseObject(bytes, User.class);
PropertyFilter propertyFilter = new PropertyFilter() {
@Override
public boolean apply(Object object, String name, Object value) {
return !"password".equals(name);
}
};
String json = JSON.toJSONString(user, propertyFilter);
JSONReader reader = new JSONReader(new StringReader(largeJson));
reader.startObject();
while (reader.hasNext()) {
String key = reader.readString();
if ("neededField".equals(key)) {
String value = reader.readString();
} else {
reader.skipValue();
}
}
reader.endObject();
*注意:性能优化需要根据具体场景进行测试和验证,不同的框架在不同场景下可能有不同的表现。建议使用 JMH 等基准测试工具进行性能对比。
最佳实践
选择合适的框架
- Spring 项目:优先考虑 Jackson,与 Spring 框架集成良好。
- 对性能要求极高:可考虑 Fastjson(注意安全问题)。
- 追求 API 简洁和易用性:可选择 Gson。
代码组织最佳实践
建议封装统一的 JSON 工具类,避免在每个业务层重复配置。
public class JsonUtils {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static String toJson(Object obj) {
try {
return OBJECT_MAPPER.writeValueAsString(obj);
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON 序列化失败", e);
}
}
public static <T> T fromJson(String json, Class<T> clazz) {
try {
return OBJECT_MAPPER.readValue(json, clazz);
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON 反序列化失败", e);
}
}
public static <T> List<T> fromJsonList(String json, Class<T> clazz) {
try {
return OBJECT_MAPPER.readValue(
json,
OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz)
);
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON 列表反序列化失败", e);
}
}
}
错误处理和异常管理
try {
User user = JsonUtils.fromJson(jsonString, User.class);
} catch (RuntimeException e) {
log.error("JSON 解析失败:{}", jsonString, e);
throw new BusinessException("数据格式错误,请检查输入");
}
try {
ObjectMapperHolder.getObjectMapper().readTree(jsonString);
} catch (Exception e) {
throw new IllegalArgumentException("无效的 JSON 格式");
}
安全处理
处理外部输入的 JSON 数据时,务必注意安全问题,特别是反序列化操作可能导致远程代码执行漏洞。
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.registerModule(new SimpleModule()
.addDeserializer(Object.class, new SafeObjectDeserializer()));
ParserConfig.getGlobalInstance().setAutoTypeSupport(false);
ParserConfig.getGlobalInstance().addAccept("com.example.model.");
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new SafeTypeAdapterFactory())
.create();
处理日期时间
不同框架对日期的处理方式略有差异,统一配置能减少很多麻烦。
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer())
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeDeserializer())
.create();
String json = JSON.toJSONString(dateObject, SerializerFeature.WriteDateUseDateFormat);
JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
String formattedJson = JSON.toJSONString(dateObject, SerializerFeature.WriteDateUseDateFormat);
常见问题与解决方案
字段名不匹配
问题:JSON 中的字段名与 Java 类中的字段名不一致,导致反序列化失败。
- Gson: 使用
@SerializedName 注解
- Jackson: 使用
@JsonProperty 注解
- Fastjson: 使用
@JSONField 注解
处理 null 值
问题:JSON 中包含 null 值,如何控制其序列化和反序列化行为。
- Gson: 使用
GsonBuilder.serializeNulls() 控制
- Jackson: 使用
@JsonInclude(JsonInclude.Include.NON_NULL) 注解或配置
- Fastjson: 使用
SerializerFeature.WriteMapNullValue 或 @JSONField 注解
循环引用问题
问题:对象之间存在循环引用,导致序列化时出现 StackOverflowError。
- Gson: 默认处理循环引用,但会生成 $ref 引用标记
- Jackson: 使用
@JsonManagedReference 和 @JsonBackReference 注解
- Fastjson: 使用
SerializerFeature.DisableCircularReferenceDetect 禁用循环引用检测
处理复杂嵌套结构
问题:JSON 结构复杂,难以直接映射到 Java 类。
- Gson: 使用
JsonParser 和 JsonElement API
- Jackson: 使用
JsonNode 树模型 API
- Fastjson: 使用
JSONObject 和 JSONArray API
性能问题
- 重用解析器实例
- 使用流式 API 而不是树模型
- 避免不必要的字符串转换
- 使用更高效的 JSON 库(如需要高性能可考虑 Fastjson)
总结与建议
选择建议
- Spring 项目:优先使用 Jackson,与 Spring 框架集成良好
- 对性能要求极高的场景:考虑 Fastjson,但需注意安全问题
- 追求简洁 API:Gson 是不错的选择
- 混合环境:可以根据不同模块的需求选择不同的框架
重要提示
- 始终使用最新稳定版本的 JSON 库,以获取安全补丁和性能改进
- 在处理外部输入的 JSON 数据时,务必注意安全问题
- 对于复杂的 JSON 处理需求,考虑封装统一的工具类
- 在关键性能路径上,进行性能测试以选择最优方案
通过本文介绍的技巧和最佳实践,相信您能够在 Java 项目中更加高效、安全地处理 JSON 数据。选择合适的工具,遵循最佳实践,将使您的代码更加健壮和可维护。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online