上一章咱們簡單的介紹了Springboot的事務控制,這一章咱們來在springboot中使用一下Gson。java
本項目的GitHub:https://github.com/pc859107393/Go2SpringBoot.gitgit
有興趣交流springboot進行快速開發的同窗能夠加一下下面的企鵝羣。github
在互聯網上面有不少關於Gson的使用介紹,在這裏我直接貼出kotlin版本的GsonUtil,具體代碼以下:web
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)。spring
SpringMVC的消息轉換器HttpMessageConverterjson
SpringMVC使用消息轉換器(HttpMessageConverter)實現將請求信息轉換爲對象、將對象轉換爲響應信息。數組
衆所周知,http請求的數據交換徹底是依靠數據流的讀寫來實現的。在servlet中咱們是直接使用ServletRequest或者ServletResponse的輸入輸出流完成一些操做。可是在SPringMVC中咱們使用一些註解來完成相關操做,具體使用的註解在org.springframework.web.bind.annotation
下面。固然咱們經常使用的一些如:@RequestBody
、@RequestPart
、@RequestParam
等等。springboot
使用了@RequestBody
後HttpMessageConverter
的相關實現類就會把數據轉換到對應的變量中(@RequestBody
標記的某個請求的請求體能夠將json自動換轉爲對應的實體,固然@ResponseBody
也是由HttpMessageConverter
相關類來轉換)。 具體的代碼請看org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
,這裏暫且不表。mvc
接着咱們向下看,能夠找到一個FormHttpMessageConverter
的實現類,顧名思義就是表單信息轉換的轉換器。可是具體的內容太多,咱們接着查找相關的實現類AllEncompassingFormHttpMessageConverter
,具體的代碼以下:app
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,是否是一切恢復原狀了?好的今天的東西已經說完了,讓咱們在結束的時候再安利一下本身!
天天進步一點點,十年磨一劍。加油!