在 Java 开发中,我们经常使用 Map<String, Object> 来存储不同类型的值,并通过 Fastjson 进行序列化和反序列化。然而,在反序列化时,Object 类型的值可能会发生变化,比如 Long 变成 Integer,导致数据不一致。本文将探讨 Fastjson 如何保证 Object 类型的一致性,并提供相应的解决方案。
1. Fastjson 默认的序列化和反序列化行为
(1)基本数据类型的处理
Fastjson 默认可以正确处理 Java 的基本数据类型,例如 Integer、Long、Double、String 等。
示例
import com.alibaba.fastjson.JSON;
import java.util.HashMap;
import java.util.Map;
public class FastjsonExample {
public static void main(String[] args) {
Map<String, Object> data = new HashMap<>();
data.put("intValue", 100);
data.put("longValue", 100L);
data.put("doubleValue", 99.99);
data.put("stringValue", "Hello, Fastjson");
// 序列化
String json = JSON.toJSONString(data);
System.out.println("Serialized JSON: " + json);
// 反序列化
Map<String, Object> deserializedData = JSON.parseObject(json, Map.class);
System.out.println("Deserialized Map: " + deserializedData);
}
}
输出
Serialized JSON: {"doubleValue":99.99,"intValue":100,"longValue":100,"stringValue":"Hello, Fastjson"}
Deserialized Map: {doubleValue=99.99, intValue=100, longValue=100, stringValue=Hello, Fastjson}
问题:
- 在反序列化时,
longValue变成了Integer,因为 JSON 默认会将没有超出Integer.MAX_VALUE的整数解析为Integer。 - 这可能导致
Object的类型与原始类型不匹配,影响后续的类型转换。
2. 解决方案
方案 1:使用 SerializerFeature.WriteClassName 记录类型信息
Fastjson 提供了 SerializerFeature.WriteClassName 选项,可以在 JSON 序列化时添加 @type 字段,记录对象的实际类型。这样在反序列化时,Fastjson 可以根据 @type 字段恢复原始类型。
示例
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import java.util.HashMap;
import java.util.Map;
public class FastjsonTypeInfoExample {
public static void main(String[] args) {
Map<String, Object> data = new HashMap<>();
data.put("intValue", 100);
data.put("longValue", 100L);
data.put("doubleValue", 99.99);
data.put("stringValue", "Hello, Fastjson");
// 序列化时记录类型信息
String json = JSON.toJSONString(data, SerializerFeature.WriteClassName);
System.out.println("Serialized JSON with type info: " + json);
// 反序列化
Map<String, Object> deserializedData = JSON.parseObject(json, Map.class);
System.out.println("Deserialized Map: " + deserializedData);
System.out.println("Type of longValue: " + deserializedData.get("longValue").getClass().getName());
}
}
输出
Serialized JSON with type info: {
"doubleValue":{"@type":"java.lang.Double","value":99.99},
"intValue":{"@type":"java.lang.Integer","value":100},
"longValue":{"@type":"java.lang.Long","value":100},
"stringValue":{"@type":"java.lang.String","value":"Hello, Fastjson"}
}
Deserialized Map: {doubleValue=99.99, intValue=100, longValue=100, stringValue=Hello, Fastjson}
Type of longValue: java.lang.Long
优点:
- 反序列化后,
longValue仍然是Long,不会变成Integer。 - 适用于
Map<String, Object>场景,确保Object类型数据不丢失。
缺点:
- JSON 变长了,增加了额外的
@type信息。 - 可能导致 JSON 解析时需要额外的安全处理(防止
@type被恶意篡改)。
方案 2:使用 parseObject 指定反序列化目标类型
如果你不想在 JSON 里记录 @type,但仍然希望在反序列化时确保 Object 类型正确,你可以显式指定 TypeReference<Map<String, Object>>,避免 Fastjson 自动推断类型。
示例
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import java.util.HashMap;
import java.util.Map;
public class FastjsonTypeReferenceExample {
public static void main(String[] args) {
Map<String, Object> data = new HashMap<>();
data.put("longValue", 100L);
// 序列化
String json = JSON.toJSONString(data);
System.out.println("Serialized JSON: " + json);
// 使用 TypeReference 反序列化
Map<String, Object> deserializedData = JSON.parseObject(json, new TypeReference<Map<String, Object>>() {});
System.out.println("Type of longValue: " + deserializedData.get("longValue").getClass().getName());
}
}
输出
Serialized JSON: {"longValue":100}
Type of longValue: java.lang.Long
优点:
- 不需要修改 JSON 格式,减少额外的
@type信息。 - 避免
Long变成Integer,保证数据一致性。
缺点:
- 适用于固定结构的 Map,不能用于复杂的对象层次结构。
3. 总结
| 方案 | 方式 | 适用场景 | 优缺点 |
|---|---|---|---|
| 默认行为 | JSON.parseObject(json, Map.class) | 适用于大多数情况,但可能导致 Long 变 Integer | 可能导致 Object 类型不一致 |
WriteClassName | JSON.toJSONString(obj, SerializerFeature.WriteClassName) | 适用于 Map<String, Object>,需要保存类型信息 | JSON 变长,可能存在安全风险 |
TypeReference | JSON.parseObject(json, new TypeReference<Map<String, Object>>() {}) | 适用于 Map,且不想修改 JSON 结构 | 不能处理复杂的多态对象 |
在实际应用中:
- 如果你希望 严格保持
Object的类型,建议使用SerializerFeature.WriteClassName方案。 - 如果你 不想修改 JSON 格式,但仍然想保证
Long不变成Integer,可以使用TypeReference。
通过合理选择序列化和反序列化策略,你可以确保 Fastjson 处理 Map<String, Object> 时不会丢失 Object 类型信息,从而提高数据一致性和安全性。
