一 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;
}
}
看看它跑出了什么结果:
{"com.bubbling.test.A@7852e922":"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的各部分就差不多完备了。