In the previous chapter, we briefly introduced transaction control in Springboot. In this chapter, we will use Gson in Springboot.

The objective of the GitHub:https://github.com/pc859107393/Go2SpringBoot.git

Those interested in springboot for rapid development can add the penguin colony below.

Simple use of Gson

There are a lot of tutorials on Gson on the Internet. Here I directly post the kotlin version of GsonUtil as follows:

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


    /** * Custom TypeAdapter,null objects will be resolved to an empty string */
    private val STRING = object : TypeAdapter<String>() {
        override fun read(reader: JsonReader): String {
            try {
                if (reader.peek() == JsonToken.NULL) {
                    reader.nextNull()
                    return ""// Return an empty string instead of 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()
            }

        }
    }

    /** * If the Int type is Double, then cast to Int */ if the Int type is Double, then cast to 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) // Replace all STRING null with STRING ""
        gsonBulder.registerTypeAdapter(Int: :class.javaPrimitiveType.INTEGER) //int is compatible with float
        gsonBulder.setDateFormat("yyyy-MM-dd HH:mm:ss")
        // Get the instanceCreators attribute by reflection
        try {
            val builder = gsonBulder.javaClass as Class<*>
            val f = builder.getDeclaredField("instanceCreators")
            f.isAccessible = true
            // Register the array's handler
            gsonBulder.registerTypeAdapterFactory(CollectionTypeAdapterFactory(ConstructorConstructor(f.get(gsonBulder) as Map<Type, InstanceCreator<Any>>)))
        } catch (e: Exception) {
            e.printStackTrace()
        }

        gson = gsonBulder.create()
    }

    /** * Json string is converted to the specified object **@paramJson Json string *@paramType Object type *@param<T> Object type *@return
     * @throws JsonSyntaxException
    </T> */
    @Throws(JsonSyntaxException::class)
    fun <T> toBean(json: String, type: Class<T>): T {
        returngson!! .fromJson(json, type) }/** * Convert jsonStr to javaBean **@param object
     * @return json string
     */
    fun toJson(`object` :Any): String {
        returngson!! .toJson(`object`)}/** * Convert jsonStr to javaBean **@param json
     * @param type
     * @return instance of type
     */
    fun <V> fromJson(json: String, type: Class<V>): V {
        returngson!! .fromJson(json, type) }/** * Convert jsonStr to javaBean **@param json
     * @param type
     * @return instance of type
     */
    fun <V> fromJson(json: String, type: Type): V {
        returngson!! .fromJson(json, type) }/** * Convert reader to javaBean **@param reader
     * @param type
     * @return instance of type
     */
    fun <V> fromJson(reader: Reader, type: Class<V>): V {
        returngson!! .fromJson(reader, type) }/** * Convert reader to javaBean **@param reader
     * @param type
     * @return instance of type
     */
    fun <V> fromJson(reader: Reader, type: Type): V {
        returngson!! .fromJson(reader, type) }/** * Convert json collection to ArrayList **@paramJson Json collection to be converted *@paramType Outgoing type */
    fun <T> toList(json: String, type: Class<T>): ArrayList<T>? {
        val list = ArrayList<T>()
        return try {
            valparser = JsonParser() parser.parse(json).asJsonArray.forEach { element -> list.add(gson!! .fromJson(element, type)) } ArrayList(list) }catch (e: Exception) {
            e.printStackTrace()
            null}}}Copy the code

Yes, it’s just a gson utility class that Kotlin uses to simplify your Gson operations, but what’s the core of what we’re doing? Parsing json data (HttpMessageConverter) using Gson in SpringMVC.

SpringMVC’s message converter HttpMessageConverter

SpringMVC uses a message converter (HttpMessageConverter) to convert request information into objects and objects into response information.

As is known to all, the data exchange of HTTP requests is completely realized by reading and writing data streams. In servlets we use the input/output stream of the ServletRequest or ServletResponse directly to perform some operations. But we use in for SPringMVC some annotations to complete the relevant operation, the specific use of annotations in the org. Springframework. Web. Bind. Under the annotation. Of course, we often use some such as @requestBody, @requestPart, @requestParam, etc.

Using @RequestBody, the associated implementation class of HttpMessageConverter converts the data into the corresponding variable (the body of a request in the @RequestBody tag can automatically convert json to the corresponding entity, Of course @responseBody is also converted by the HttpMessageConverter class. Specific code see org. Springframework. Web. Servlet. MVC) method. The annotation. RequestResponseBodyMethodProcessor, here not for a moment.

Then we look down to find an implementation class for FormHttpMessageConverter, which is, as the name suggests, a converter for converting form information. But the content is too much, we went on to find the relevant implementation class AllEncompassingFormHttpMessageConverter, specific code is as follows:

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(a) {
		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(newMappingJackson2SmileHttpMessageConverter()); }}}Copy the code

From the above we can see, the SpringMVC HttpMessage converter in the queue have been joined by Jackson and Gson parser, so we’re going to use Gson to parse, you just need to remove MappingJackson2HttpMessageConverter.

Next we need to manipulate the MessageConverters data in WebMvcConfigurer. The code is as follows:

@SpringBootApplication
@EnableWebMvc
@EnableSwagger2
@MapperScan(value = ["cn.acheng1314.base.dao"])
@Configuration
@EnableTransactionManagement
class BaseApplication : WebMvcConfigurer {

    override fun extendMessageConverters(converters: MutableList<HttpMessageConverter< * > >) {
        / / delete MappingJackson2HttpMessageConverter and use GsonHttpMessageConverter replacement
        converters.forEach { t: HttpMessageConverter<*>? ->
            if (t is MappingJackson2HttpMessageConverter) {
                converters.remove(t)
                return super.extendMessageConverters(converters)
            }
        }
    }
    
}
Copy the code

At this point we run through the project, implementing Gson parsing JSON is no problem at all, but new problems arise!

Error: In our swagger, we can’t parse json correctly. If we look at the web debug information, we can’t parse JSON correctly. Do you think we’re in a hole? Yes, yes, we through a search can be seen in the SpringFox bag SpringFox. Documentation. Spring. Web. The json and under the json JsonSerializer are realized by Jackson, the code is as follows:

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(a) {
    returnvalue; }}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); }}}Copy the code

Therefore, it is suggested that all json nodes implemented by Gson need to be disguised as Jackson, and the processing code is as follows:

import java.lang.reflect.Type

import com.google.gson.*
import springfox.documentation.spring.web.json.Json
/** * Implement your own 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())
        }

}
Copy the code

The same in our WebMvcConfigurer implementation class rewritten extendMessageConverters method should use our SpringfoxJsonToGsonAdapter here, the code is as follows:

    override fun extendMessageConverters(converters: MutableList<HttpMessageConverter< * > >) {
        / / delete MappingJackson2HttpMessageConverter and use GsonHttpMessageConverter replacement
        converters.forEach { t: HttpMessageConverter<*>? ->
            if (t is MappingJackson2HttpMessageConverter) {
                converters.remove(t)
                converters.add(object : GsonHttpMessageConverter() {
                    init {
                        // Customize the Gson adapter
                        super.setGson(GsonBuilder()
                                .registerTypeAdapter(Json::class.java.SpringfoxJsonToGsonAdapter())
                                .create())
                    }
                }) / / add GsonHttpMessageConverter
                return super.extendMessageConverters(converters)
            }
        }
    }
Copy the code

Now let’s try swagger again. Is everything back to normal? Ok, today’s things have been said, let us at the end of the time amway again!

Every day a little progress, ten years sharpen a sword. Come on!