完整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封裝的主要處理邏輯:
- 內部單例設計GsonBuilder,並提供getGson()方法返回由GsonBuilder物件建立Gson物件;
- 提供一個解析JSON字串到Java物件的方法;
- 開啟複雜Map解析邏輯;
- 設定對日期格式進行序列化及反序列化的樣式
大致結構如下:
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的各部分就差不多完備了。