1. 程式人生 > >完整JavaWeb專案筆記 第四部分-Gson再封裝

完整JavaWeb專案筆記 第四部分-Gson再封裝

文章目錄

一 Gson工具設計的必要性

  Gson是什麼,做什麼用的這裡不多贅述,但凡做過Java的朋友都不陌生,如果你真的沒用過,請網路搜尋下。

  這裡要說明的是一些略微複雜的用法,我們決定統一應答為JSON格式,那麼就需要對IResponse物件進行JSON序列化,因為IResponse物件內部有一個Object物件,它的結構在執行時刻是可以是任意型別的,所以我們有必要對Gson進行一些配置,使之能夠正確的解析。

  另外,在其他可能的應用層面,如果需要使用JSON的序列化及反序列化,我們依然有理由對Gson進行一次封裝,能讓它工作的更加方便,有效,且適用範圍更廣。

二 Gson序列化及反序列化時指定樣式

  Gson在序列化基本型別的成員時,如果不指定任何序列化樣式(將被叫做重新命名,可以通過@SerializedName註解實現),其依然可以發揮出很好的作用,但是當其作用在引用型別的成員上時就會出現一些尷尬的情況,我舉個例子,當Gson序列化一個Map的時候:

package com.bubbling.test;

import java.util.HashMap;
import java.
util.Map; import com.google.gson.Gson; public class Test { public static void main(String[] args) { Map<A, String> map = new HashMap<A, String>(); map.put(new A("first"), "first"); Gson gson = new Gson(); String str = gson.toJson(map); System.out.println(str); } } class A
{ String name; public A(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }

  看看它跑出了什麼結果:

{"[email protected]":"first"}

  有趣!看樣子這是A類物件的Hash地址,事實上也的確是這樣的,因為Gson在序列化Map的時候,Map的Key值序列化採用的是Key成員的toString()方法,如果該成員型別沒有重寫過toString(),那麼則拿來物件的Hash地址。

  讓我們驗證下,將上例中的A類重寫toString():

package com.bubbling.test;

import java.util.HashMap;
import java.util.Map;

import com.google.gson.Gson;

public class Test
{
	public static void main(String[] args)
	{
		Map<A, String> map = new HashMap<A, String>();
		map.put(new A("first"), "first");
		Gson gson = new Gson();
		String str = gson.toJson(map);
		System.out.println(str);
	}
}

class A
{
	String name;

	public A(String name)
	{
		super();
		this.name = name;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	@Override
	public String toString()
	{
		return "A [name=" + name + "]";
	}

}

  這一次的結果如下:

{"A [name\u003dfirst]":"first"}

  不出意外的,確實是toString()重寫後的輸出結果。顯然我們不希望會出現這樣的意外情況,即使我們遺漏了某些型別的toString()重寫,甚至我們toString()寫的不夠出色,我們依然希望Gson能夠序列化Map這樣的複雜物件,所以有了今天的第四部分。

  首先我們需要開啟一個標誌位——enableComplexMapKeySerialization()該方法由GsonBuilder物件呼叫,複雜的序列化要求我們不能通過new Gson()的方式來獲取Gson物件,而GsonBuilder提供了獲取Gson物件的方法,並且提供對其設定序列化樣式的介面。

  任何我們需要進行復雜序列化的型別,我們都可以通過實現JsonSerializer介面(同樣的,如果設定反序列化樣式,則實現JsonDeserializer介面)來建立一個序列化器物件,並新增到GsonBuilder,這樣由GsonBuilder建立的Gson物件便獲取的強大的序列化能力。

  這裡我再舉一個例子,Gson在序列化Date時,會出現非常意外的情況,如下:

package com.bubbling.test;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import com.google.gson.Gson;

public class Test
{
	public static void main(String[] args)
	{
		Date date = Calendar.getInstance().getTime();
		Gson gson = new Gson();
		String str = gson.toJson(date);
		System.out.println(str);

//		Map<A, String> map = new HashMap<A, String>();
//		map.put(new A("first"), "first");
//		Gson gson = new Gson();
//		String str = gson.toJson(map);
//		System.out.println(str);
	}
}

class A
{
	String name;

	public A(String name)
	{
		super();
		this.name = name;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	@Override
	public String toString()
	{
		return "A [name=" + name + "]";
	}

}

  執行結果如下:

"Dec 26, 2018 8:37:58 AM"

  這一定不是我們想要的,按照上述介紹的方法,JsonSerializer,為Date型別指定格式化樣式為yyyyMMddHHmmss,我們來看看情況如何:

package com.bubbling.test;

import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

public class Test
{
	public static void main(String[] args)
	{
		GsonBuilder builder = new GsonBuilder();
		builder.registerTypeAdapter(Date.class, new JsonSerializer<Date>()
		{
			@Override
			public JsonElement serialize(Date date, Type type, JsonSerializationContext context)
			{
				String result = null;

				if (date != null)
				{
					result = new SimpleDateFormat("yyyyMMddHHmmss").format(date);
				}

				return new JsonPrimitive(result);
			}
		});

		Date date = Calendar.getInstance().getTime();
		Gson gson = builder.create();
		String str = gson.toJson(date);
		System.out.println(str);
	}
}

  這樣我們再看看結果如何:

"20181226084448"

  完美!

三 封裝GsonUtil類

  根據上面的案例,我們基本上能夠確定了對Gson封裝的主要處理邏輯:

  1. 內部單例設計GsonBuilder,並提供getGson()方法返回由GsonBuilder物件建立Gson物件;
  2. 提供一個解析JSON字串到Java物件的方法;
  3. 開啟複雜Map解析邏輯;
  4. 設定對日期格式進行序列化及反序列化的樣式

  大致結構如下:

package com.bubbling.util;

import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

/**
 * 設定日期的序列化樣式,如果日期為空則返回Null,如果不為空則按yyyyMMddHHmmss樣式處理
 * 
 * @author 胡楠
 *
 */
class DateSerializer implements JsonSerializer<Date>
{
	public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context)
	{
		String result = "NULL";

		if (src != null)
		{
			result = new SimpleDateFormat("yyyyMMddHHmmss").format(src);
		}

		return new JsonPrimitive(result);
	}
}

/**
 * 反序列化日期時以yyyyMMddHHmmss樣式通過SimpleDateFormat進行parse
 * 
 * @author 胡楠
 *
 */
class DateDeserializer implements JsonDeserializer<Date>
{
	public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
			throws JsonParseException
	{
		Date result = null;

		if (json == null)
		{
			return null;
		}

		String input = json.getAsJsonPrimitive().getAsString();

		if (input == null || input.equalsIgnoreCase("NULL"))
		{
			return null;
		}

		try
		{
			result = new SimpleDateFormat("yyyyMMddHHmmss").parse(input);
		}
		catch (ParseException e)
		{
		}

		return result;
	}
}

public class GsonUtil
{
	private static GsonBuilder gsonBuilder = new GsonBuilder();

	static
	{
		gsonBuilder.registerTypeAdapter(Date.class, new DateSerializer());
		gsonBuilder.registerTypeAdapter(Date.class, new DateDeserializer());
		gsonBuilder.enableComplexMapKeySerialization();
		gsonBuilder.serializeNulls();
	}

	private GsonUtil()
	{
	}

	public static Gson getGson()
	{
		Gson result = null;

		synchronized (gsonBuilder)
		{
			result = gsonBuilder.create();
		}

		return result;
	}

	public static JsonObject stringToGsonObject(String str)
	{
		return new JsonParser().parse(str).getAsJsonObject();
	}

	public static <T> T parseData(String data, Class<T> classOfT)
	{
		T result = null;

		if (data != null)
		{
			try
			{
				result = getGson().fromJson(data, classOfT);
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}

		return result;
	}

	/**
	 * 多用於解析
	 * 
	 * @param data
	 * @param typeOfT
	 *            new TypeToken(Class){}.getType()
	 * @return
	 */
	public static <T> T parseData(String data, Type typeOfT)
	{
		T result = null;

		if (data != null)
		{
			Gson gson = getGson();
			try
			{
				result = gson.fromJson(data, typeOfT);
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}

		return result;
	}
}

  這一部分主要是針對應答拼裝時需要進行的序列化處理,接下來我們還需要對請求分發進行處理,這樣我們核心Servlet的各部分就差不多完備了。