完整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;
	}
}

  看看它跑出了什么结果:

{"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封装的主要处理逻辑:

  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的各部分就差不多完备了。

猜你喜欢

转载自blog.csdn.net/o983950935/article/details/85261473
今日推荐