上一章我们简单的介绍了Springboot的事务控制,这一章我们来在springboot中使用一下Gson。
本项目的GitHub:https://github.com/pc859107393/Go2SpringBoot.git
有兴趣交流springboot进行快速开发的同学可以加一下下面的企鹅群。
Gson的简单使用
在互联网上面有很多关于Gson的使用介绍,在这里我直接贴出kotlin版本的GsonUtil,具体代码如下:
import com.google.gson.*
import java.io.IOException
import java.io.Reader
import java.lang.reflect.Type
import java.util.ArrayList
@SuppressWarnings("unchecked")
object GsonUtil {
private var gson: Gson? = null
/**
* 自定义TypeAdapter ,null对象将被解析成空字符串
*/
private val STRING = object : TypeAdapter<String>() {
override fun read(reader: JsonReader): String {
try {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull()
return ""//原先是返回Null,这里改为返回空字符串
}
return reader.nextString()
} catch (e: Exception) {
e.printStackTrace()
}
return ""
}
override fun write(writer: JsonWriter, value: String?) {
try {
if (value == null) {
writer.nullValue()
return
}
writer.value(value)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
/**
* 自定义adapter,解决由于数据类型为Int,实际传过来的值为Float,导致解析出错的问题
* 目前的解决方案为将所有Int类型当成Double解析,再强制转换为Int
*/
private val INTEGER = object : TypeAdapter<Number>() {
@Throws(IOException::class)
override fun read(`in`: JsonReader): Number {
if (`in`.peek() == JsonToken.NULL) {
`in`.nextNull()
return 0
}
try {
val i = `in`.nextDouble()
return i.toInt()
} catch (e: NumberFormatException) {
throw JsonSyntaxException(e)
}
}
@Throws(IOException::class)
override fun write(out: JsonWriter, value: Number) {
out.value(value)
}
}
init {
val gsonBulder = GsonBuilder()
gsonBulder.registerTypeAdapter(String::class.java, STRING) //所有String类型null替换为字符串“”
gsonBulder.registerTypeAdapter(Int::class.javaPrimitiveType, INTEGER) //int类型对float做兼容
gsonBulder.setDateFormat("yyyy-MM-dd HH:mm:ss")
//通过反射获取instanceCreators属性
try {
val builder = gsonBulder.javaClass as Class<*>
val f = builder.getDeclaredField("instanceCreators")
f.isAccessible = true
//注册数组的处理器
gsonBulder.registerTypeAdapterFactory(CollectionTypeAdapterFactory(ConstructorConstructor(f.get(gsonBulder) as Map<Type, InstanceCreator<Any>>)))
} catch (e: Exception) {
e.printStackTrace()
}
gson = gsonBulder.create()
}
/**
* Json字符串 转为指定对象
*
* @param json json字符串
* @param type 对象类型
* @param <T> 对象类型
* @return
* @throws JsonSyntaxException
</T> */
@Throws(JsonSyntaxException::class)
fun <T> toBean(json: String, type: Class<T>): T {
return gson!!.fromJson(json, type)
}
/**
* 将jsonStr转换为javaBean
*
* @param object
* @return json string
*/
fun toJson(`object`: Any): String {
return gson!!.toJson(`object`)
}
/**
* 将jsonStr转换为javaBean
*
* @param json
* @param type
* @return instance of type
*/
fun <V> fromJson(json: String, type: Class<V>): V {
return gson!!.fromJson(json, type)
}
/**
* 将jsonStr转换为javaBean
*
* @param json
* @param type
* @return instance of type
*/
fun <V> fromJson(json: String, type: Type): V {
return gson!!.fromJson(json, type)
}
/**
* 将reader转换为javaBean
*
* @param reader
* @param type
* @return instance of type
*/
fun <V> fromJson(reader: Reader, type: Class<V>): V {
return gson!!.fromJson(reader, type)
}
/**
* 将reader转换为javaBean
*
* @param reader
* @param type
* @return instance of type
*/
fun <V> fromJson(reader: Reader, type: Type): V {
return gson!!.fromJson(reader, type)
}
/**
* 将json集合转换为ArrayList
*
* @param json 需要转换的json集合
* @param type 转出的类型
*/
fun <T> toList(json: String, type: Class<T>): ArrayList<T>? {
val list = ArrayList<T>()
return try {
val parser = JsonParser()
parser.parse(json).asJsonArray.forEach { element -> list.add(gson!!.fromJson(element, type)) }
ArrayList(list)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}
是的没错,上面仅仅是一个kotlin上面用的gson的工具类,简化你的gson操作,但是我们的核心是什么?在SpringMVC中使用Gson解析json数据(HttpMessageConverter)。
SpringMVC的消息转换器HttpMessageConverter
SpringMVC使用消息转换器(HttpMessageConverter)实现将请求信息转换为对象、将对象转换为响应信息。
众所周知,http请求的数据交换完全是依靠数据流的读写来实现的。在servlet中我们是直接使用ServletRequest或者ServletResponse的输入输出流完成一些操作。但是在SPringMVC中我们使用一些注解来完成相关操作,具体使用的注解在org.springframework.web.bind.annotation
下面。当然我们常用的一些如:@RequestBody
、@RequestPart
、@RequestParam
等等。
使用了@RequestBody
后HttpMessageConverter
的相关实现类就会把数据转换到对应的变量中(@RequestBody
标记的某个请求的请求体可以将json自动换转为对应的实体,当然@ResponseBody
也是由HttpMessageConverter
相关类来转换)。 具体的代码请看org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
,这里暂且不表。
接着我们向下看,可以找到一个FormHttpMessageConverter
的实现类,顾名思义就是表单信息转换的转换器。但是具体的内容太多,我们接着查找相关的实现类AllEncompassingFormHttpMessageConverter
,具体的代码如下:
public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConverter {
private static final boolean jaxb2Present =
ClassUtils.isPresent("javax.xml.bind.Binder",
AllEncompassingFormHttpMessageConverter.class.getClassLoader());
private static final boolean jackson2Present =
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
AllEncompassingFormHttpMessageConverter.class.getClassLoader()) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
AllEncompassingFormHttpMessageConverter.class.getClassLoader());
private static final boolean jackson2XmlPresent =
ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper",
AllEncompassingFormHttpMessageConverter.class.getClassLoader());
private static final boolean jackson2SmilePresent =
ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory",
AllEncompassingFormHttpMessageConverter.class.getClassLoader());
private static final boolean gsonPresent =
ClassUtils.isPresent("com.google.gson.Gson",
AllEncompassingFormHttpMessageConverter.class.getClassLoader());
private static final boolean jsonbPresent =
ClassUtils.isPresent("javax.json.bind.Jsonb",
AllEncompassingFormHttpMessageConverter.class.getClassLoader());
public AllEncompassingFormHttpMessageConverter() {
addPartConverter(new SourceHttpMessageConverter<>());
if (jaxb2Present && !jackson2XmlPresent) {
addPartConverter(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
addPartConverter(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
addPartConverter(new GsonHttpMessageConverter());
}
else if (jsonbPresent) {
addPartConverter(new JsonbHttpMessageConverter());
}
if (jackson2XmlPresent) {
addPartConverter(new MappingJackson2XmlHttpMessageConverter());
}
if (jackson2SmilePresent) {
addPartConverter(new MappingJackson2SmileHttpMessageConverter());
}
}
}
从上面我们可以看到SpringMVC中HttpMessage的转换器队列中已经加入了Jackson和Gson的解析器,所以我们要使用Gson来解析,只需要移除MappingJackson2HttpMessageConverter即可。
接下来我们需要在WebMvcConfigurer
中去操作MessageConverters的数据,代码如下:
@SpringBootApplication
@EnableWebMvc
@EnableSwagger2
@MapperScan(value = ["cn.acheng1314.base.dao"])
@Configuration
@EnableTransactionManagement
class BaseApplication : WebMvcConfigurer {
override fun extendMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
// 删除MappingJackson2HttpMessageConverter,并使用GsonHttpMessageConverter替换
converters.forEach { t: HttpMessageConverter<*>? ->
if (t is MappingJackson2HttpMessageConverter) {
converters.remove(t)
return super.extendMessageConverters(converters)
}
}
}
}
这个时候我们跑一下项目,实现Gson解析json是完全没有问题的,但是新的问题产生了!
问题:在我们的swagger中,不能正确的解析json了,仔细看一下web的调试信息,会提示我们不能正确的解析json,emmm。。。是不是觉得坑来了?是的,没错,我们通过一番查找可以看到在SpringFox的包springfox.documentation.spring.web.json
下的Json
和JsonSerializer
均是采用的Jackson实现,代码如下:
package springfox.documentation.spring.web.json;
import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.annotation.JsonValue;
public class Json {
private final String value;
public Json(String value) {
this.value = value;
}
@JsonValue
@JsonRawValue
public String value() {
return value;
}
}
package springfox.documentation.spring.web.json;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
public class JsonSerializer {
private ObjectMapper objectMapper = new ObjectMapper();
public JsonSerializer(List<JacksonModuleRegistrar> modules) {
for (JacksonModuleRegistrar each : modules) {
each.maybeRegisterModule(objectMapper);
}
}
public Json toJson(Object toSerialize) {
try {
return new Json(objectMapper.writeValueAsString(toSerialize));
} catch (JsonProcessingException e) {
throw new RuntimeException("Could not write JSON", e);
}
}
}
所以这里就提示我们需要做到Gson实现的json节点都需要伪装成Jackson的样子,处理代码如下:
import java.lang.reflect.Type
import com.google.gson.*
import springfox.documentation.spring.web.json.Json
/**
* 实现自己的JsonSerializer
*/
class SpringfoxJsonToGsonAdapter : com.google.gson.JsonSerializer<Json> {
override fun serialize(json: Json, type: Type, context: JsonSerializationContext): JsonElement {
val parser = JsonParser()
return parser.parse(json.value())
}
}
同样的在我们的WebMvcConfigurer实现类中重写extendMessageConverters方法这里应该使用我们的SpringfoxJsonToGsonAdapter,代码如下:
override fun extendMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
// 删除MappingJackson2HttpMessageConverter,并使用GsonHttpMessageConverter替换
converters.forEach { t: HttpMessageConverter<*>? ->
if (t is MappingJackson2HttpMessageConverter) {
converters.remove(t)
converters.add(object : GsonHttpMessageConverter() {
init {
//自定义Gson适配器
super.setGson(GsonBuilder()
.registerTypeAdapter(Json::class.java, SpringfoxJsonToGsonAdapter())
.create())
}
}) // 添加GsonHttpMessageConverter
return super.extendMessageConverters(converters)
}
}
}
现在我们再去试一试swagger,是不是一切恢复原状了?好的今天的东西已经说完了,让我们在结束的时候再安利一下自己!
每天进步一点点,十年磨一剑。加油!