今天来讲java.util.Map的序列化和反序列化。
当中有一个陷阱,匪夷所思,请看代码。
import java.util.Date; import java.util.Hashtable; import java.util.Map; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; public class DateGsonTest { private String name; private Date birthday; public static void main(String[] args) { Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); DateGsonTest dgt1 = new DateGsonTest(); dgt1.setName("Lance"); dgt1.setBirthday(new Date(537465600000l)); DateGsonTest dgt2 = new DateGsonTest(); dgt2.setName("Azure"); dgt2.setBirthday(new Date(573235200000l)); Map<String, DateGsonTest> dgtMap = new Hashtable<String, DateGsonTest>(); dgtMap.put(dgt1.getName(), dgt1); dgtMap.put(dgt2.getName(), dgt2); // dgtMap.put("Description", "This is a DateGsonTest map."); String dgtMapStr = gson.toJson(dgtMap); System.out.println(dgtMapStr); Map<String, DateGsonTest> dgtMapTemp = gson.fromJson(dgtMapStr, new TypeToken<Hashtable<String, DateGsonTest>>() { }.getType()); System.out.println("The json string of Lance:" + dgtMapTemp.get("Lance")); System.out.println("The json string of Azure:" + dgtMapTemp.get("Azure")); // System.out.println("The Description:" + dgtMapTemp.get("Description")); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
第32行的输出为:
{ "Azure": { "name": "Azure", "birthday": "1988-03-02 00:00:00" }, "Lance": { "name": "Lance", "birthday": "1987-01-13 00:00:00" } }
第37、38行的输出为:
The json string of Lance:DateGsonTest@51507e7 The json string of Azure:DateGsonTest@27b9d14c
程序没有任何异常,所以我想说明的并不是这个。
在某些情况下,Map中存放的数据类型并不是确定的,也不能用泛型来限定。
import java.util.Date; import java.util.Hashtable; import java.util.Map; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; public class DateGsonTest { private String name; private Date birthday; public static void main(String[] args) { Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); DateGsonTest dgt1 = new DateGsonTest(); dgt1.setName("Lance"); dgt1.setBirthday(new Date(537465600000l)); DateGsonTest dgt2 = new DateGsonTest(); dgt2.setName("Azure"); dgt2.setBirthday(new Date(573235200000l)); Map<String, Object> dgtMap = new Hashtable<String, Object>(); dgtMap.put(dgt1.getName(), dgt1); dgtMap.put(dgt2.getName(), dgt2); dgtMap.put("Description", "This is a DateGsonTest map."); String dgtMapStr = gson.toJson(dgtMap); System.out.println(dgtMapStr); Map<String, Object> dgtMapTemp = gson.fromJson(dgtMapStr, new TypeToken<Hashtable<String, Object>>() { }.getType()); System.out.println("The json string of Lance:" + dgtMapTemp.get("Lance")); System.out.println("The json string of Azure:" + dgtMapTemp.get("Azure")); System.out.println("The Description:" + dgtMapTemp.get("Description")); DateGsonTest Lance = gson.fromJson(dgtMapTemp.get("Lance").toString(), DateGsonTest.class); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
此时,Map<String, DateGsonTest> 必须修改成 Map<String, Object>
第32行的输出为:
{ "Azure": { "name": "Azure", "birthday": "1988-03-02 00:00:00" }, "Description": "This is a DateGsonTest map.", "Lance": { "name": "Lance", "birthday": "1987-01-13 00:00:00" } }
此时,这段Json字符串仍然是没有问题的。出问题的是第37、38行的输出:
The json string of Lance:{name=Lance, birthday=1987-01-13 00:00:00} The json string of Azure:{name=Azure, birthday=1988-03-02 00:00:00}
经过gson一次反序列化后,DateGsonTest 对象Json字符串上的引号莫名其妙消失了。。。然后41行妥妥的抛了一个异常:
Exception in thread "main" com.google.gson.JsonSyntaxException: 1987-01-13 at com.google.gson.DefaultDateTypeAdapter.deserializeToDate(DefaultDateTypeAdapter.java:107) at com.google.gson.DefaultDateTypeAdapter.deserialize(DefaultDateTypeAdapter.java:82) at com.google.gson.DefaultDateTypeAdapter.deserialize(DefaultDateTypeAdapter.java:35) at com.google.gson.TreeTypeAdapter.read(TreeTypeAdapter.java:58) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:117) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:217) at com.google.gson.Gson.fromJson(Gson.java:814) at com.google.gson.Gson.fromJson(Gson.java:779) at com.google.gson.Gson.fromJson(Gson.java:728) at com.google.gson.Gson.fromJson(Gson.java:700) at DateGsonTest.main(DateGsonTest.java:41) Caused by: java.text.ParseException: Unparseable date: "1987-01-13" at java.text.DateFormat.parse(DateFormat.java:357) at com.google.gson.DefaultDateTypeAdapter.deserializeToDate(DefaultDateTypeAdapter.java:105) ... 10 more
这是我在实战中碰到的真实案例简化的,当时心中仿佛有一万头草泥马在奔腾。。。
好了,现在说说一个临时的解决方案。为了保障最终DateGsonTest 对象能被正确反序列化,在DateGsonTest 放入Map之前,对象必须先被序列化一次。如下所示:
import java.util.Date; import java.util.Hashtable; import java.util.Map; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; public class DateGsonTest { private String name; private Date birthday; public static void main(String[] args) { Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); DateGsonTest dgt1 = new DateGsonTest(); dgt1.setName("Lance"); dgt1.setBirthday(new Date(537465600000l)); DateGsonTest dgt2 = new DateGsonTest(); dgt2.setName("Azure"); dgt2.setBirthday(new Date(573235200000l)); Map<String, Object> dgtMap = new Hashtable<String, Object>(); dgtMap.put(dgt1.getName(), gson.toJson(dgt1)); dgtMap.put(dgt2.getName(), gson.toJson(dgt2)); dgtMap.put("Description", "This is a DateGsonTest map."); String dgtMapStr = gson.toJson(dgtMap); System.out.println(dgtMapStr); Map<String, Object> dgtMapTemp = gson.fromJson(dgtMapStr, new TypeToken<Hashtable<String, Object>>() { }.getType()); System.out.println("The json string of Lance:" + dgtMapTemp.get("Lance")); System.out.println("The json string of Azure:" + dgtMapTemp.get("Azure")); System.out.println("The Description:" + dgtMapTemp.get("Description")); DateGsonTest Lance = gson.fromJson(dgtMapTemp.get("Lance").toString(), DateGsonTest.class); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
此时32行的输出为:
{ "Azure": "{\"name\":\"Azure\",\"birthday\":\"1988-03-02 00:00:00\"}", "Description": "This is a DateGsonTest map.", "Lance": "{\"name\":\"Lance\",\"birthday\":\"1987-01-13 00:00:00\"}" }
然后37、38行的输出为:
The json string of Lance:{"name":"Lance","birthday":"1987-01-13 00:00:00"} The json string of Azure:{"name":"Azure","birthday":"1988-03-02 00:00:00"}
世界终于清净了。。。