Gson使用心得(二):Map反序列化的一个陷阱

今天来讲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"}


世界终于清净了。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是你的春哥!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值