在 Java 开发中,我们经常使用 Map<String, Object> 来存储不同类型的值,并通过 Fastjson 进行序列化和反序列化。然而,在反序列化时,Object 类型的值可能会发生变化,比如 Long 变成 Integer,导致数据不一致。本文将探讨 Fastjson 如何保证 Object 类型的一致性,并提供相应的解决方案。


1. Fastjson 默认的序列化和反序列化行为

(1)基本数据类型的处理

Fastjson 默认可以正确处理 Java 的基本数据类型,例如 IntegerLongDoubleString 等。

示例

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)适用于大多数情况,但可能导致 LongInteger可能导致 Object 类型不一致
WriteClassNameJSON.toJSONString(obj, SerializerFeature.WriteClassName)适用于 Map<String, Object>,需要保存类型信息JSON 变长,可能存在安全风险
TypeReferenceJSON.parseObject(json, new TypeReference<Map<String, Object>>() {})适用于 Map,且不想修改 JSON 结构不能处理复杂的多态对象

在实际应用中:

  • 如果你希望 严格保持 Object 的类型,建议使用 SerializerFeature.WriteClassName 方案。
  • 如果你 不想修改 JSON 格式,但仍然想保证 Long 不变成 Integer,可以使用 TypeReference

通过合理选择序列化和反序列化策略,你可以确保 Fastjson 处理 Map<String, Object> 时不会丢失 Object 类型信息,从而提高数据一致性和安全性。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注