如何配置GSON识别ISO日期字符串并反序列化为Date对象?
解决Gson反序列化Map时自动识别日期字符串为Date对象的问题
要让Gson在反序列化Map<String, Object>时,自动将符合指定格式的字符串转换为Date对象,核心思路是自定义反序列化逻辑——默认情况下Gson处理Object类型时,只会将JSON字符串解析为String实例,不会主动尝试类型转换。以下是两种可行的实现方案:
方案一:自定义JsonDeserializer
通过实现JsonDeserializer<Object>,在反序列化时检查字符串是否匹配日期格式,符合条件则转换为Date:
1. 实现自定义反序列化器
import com.google.gson.*; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.regex.Pattern; public class DateAwareObjectDeserializer implements JsonDeserializer<Object> { // 匹配目标日期格式的正则 private static final Pattern DATE_PATTERN = Pattern.compile("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z"); // 对应日期格式解析器 private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); @Override public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString()) { String strValue = json.getAsString(); // 先通过正则过滤,避免无效解析尝试 if (DATE_PATTERN.matcher(strValue).matches()) { try { // SimpleDateFormat非线程安全,解析时需同步 synchronized (DATE_FORMAT) { return DATE_FORMAT.parse(strValue); } } catch (ParseException e) { // 解析失败,返回原字符串 return strValue; } } } // 非目标类型或不匹配格式,交给Gson默认处理 return context.deserialize(json, typeOfT); } }
2. 注册并使用自定义反序列化器
import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import java.util.Date; import java.util.HashMap; import java.util.Map; public class TestGson { public static void main(String[] args) { // 序列化测试 Map<String, Object> originalMap = new HashMap<>(); originalMap.put("now", new Date()); Gson gson = new GsonBuilder() .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") .registerTypeAdapter(Object.class, new DateAwareObjectDeserializer()) .create(); String json = gson.toJson(originalMap); System.out.println("序列化结果:" + json); // 反序列化测试 Map<String, Object> deserializedMap = gson.fromJson(json, new TypeToken<Map<String, Object>>() {}.getType()); Object nowValue = deserializedMap.get("now"); System.out.println("反序列化后类型:" + nowValue.getClass()); System.out.println("反序列化后值:" + nowValue); } }
方案二:自定义TypeAdapter(性能更优)
TypeAdapter是Gson更底层的API,直接操作JsonReader/JsonWriter,避免了JsonElement的创建开销,性能更出色:
1. 实现自定义TypeAdapter
import com.google.gson.*; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.regex.Pattern; public class DateAwareObjectTypeAdapter extends TypeAdapter<Object> { private static final Pattern DATE_PATTERN = Pattern.compile("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z"); private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); private final TypeAdapter<Object> delegateAdapter; public DateAwareObjectTypeAdapter(TypeAdapter<Object> delegateAdapter) { this.delegateAdapter = delegateAdapter; } @Override public void write(JsonWriter out, Object value) throws IOException { delegateAdapter.write(out, value); } @Override public Object read(JsonReader in) throws IOException { JsonToken token = in.peek(); if (token == JsonToken.STRING) { String strValue = in.nextString(); if (DATE_PATTERN.matcher(strValue).matches()) { try { synchronized (DATE_FORMAT) { return DATE_FORMAT.parse(strValue); } } catch (ParseException e) { return strValue; } } return strValue; } return delegateAdapter.read(in); } }
2. 通过TypeAdapterFactory注册
Gson gson = new GsonBuilder() .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") .registerTypeAdapterFactory(new TypeAdapterFactory() { @SuppressWarnings("unchecked") @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { if (type.getRawType() == Object.class) { return (TypeAdapter<T>) new DateAwareObjectTypeAdapter(gson.getDelegateAdapter(this, type)); } return null; } }) .create();
关键说明
- 正则匹配的必要性:先用正则过滤字符串,避免对所有字符串执行日期解析,提升处理效率。
- 线程安全问题:
SimpleDateFormat不是线程安全的,因此解析时必须加同步锁,或改用ThreadLocal存储每个线程独立的DateFormat实例。 - 多格式扩展:如果需要支持多种日期格式,可添加多个正则和DateFormat,依次尝试解析。
- 时区处理:原格式中的
Z代表UTC时区,若需转换为本地时区,可通过DATE_FORMAT.setTimeZone(TimeZone.getDefault())调整。
内容的提问来源于stack exchange,提问作者Emmanuel
相关产品推荐
相关产品推荐

