SpringBoot2-第四章:使用Gson做为HttpMessageConverter

上一章咱们简单的介绍了Springboot的事务控制,这一章咱们来在springboot中使用一下Gson。java

本项目的GitHub:https://github.com/pc859107393/Go2SpringBoot.gitgit

有兴趣交流springboot进行快速开发的同窗能够加一下下面的企鹅群。github

行走的java全栈

Gson的简单使用

在互联网上面有不少关于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

使用了@RequestBodyHttpMessageConverter的相关实现类就会把数据转换到对应的变量中(@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下的JsonJsonSerializer均是采用的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,是否是一切恢复原状了?好的今天的东西已经说完了,让咱们在结束的时候再安利一下本身!

天天进步一点点,十年磨一剑。加油!

相关文章
相关标签/搜索