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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值