SpringBoot default JSON resolution scheme

Posted by spicey on Sat, 15 Feb 2020 07:16:23 +0100

1. What is JSON

JSON (JavaScript Object Notation) is an open standard data exchange format based on a subset of JavaScript syntax.JSON is text-based, lightweight, and is generally considered easy to read/write.

Okay, let's not talk a lot. Here's how to use JSON in SpringBoot.

2. How to use JSON in SpringBoot

Before learning json, we must first learn about HttpMessageConverter, which is a message conversion tool by name.

Let me introduce its two functions:

1. Serialize the object returned by the server into a JSON string.
2. Deserialize the JSON string passed from the front end into a Java object.

All JSON generation is dependent on the relevant HttpMessageConverter.

SpringMVC automatically configures Jackson and Gson's HTTP MessageConverter, which is also automatically configured in Spring Boot. Here are the paths to the source code for both:

org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration
org.springframework.boot.autoconfigure.http.GsonHttpMessageConvertersConfiguration

So if the user uses jackson and gson, and there is no additional configuration, just add the dependency.

3. Examples

[Step 1] The old rule is to create a SpringBoot project first.As you can see from Maven on the right, SpringBoot has actually integrated json into it, and Maven has the following structure:

[Step 2] Create a bean and a controller class with the following project structure and code:

[Project Structure]

[User.java]

package com.mango.json.bean;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.util.Date;

public class User {

    private Integer id;
    private String username;
    private String address;
    
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

[UserController.java]

package com.mango.json.controller;

import com.mango.json.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@RestController
public class UserController {

    @GetMapping("/user")
    public List<User> getUser() {
        List<User> userList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setId(i);
            user.setUsername("mango>>>" + i);
            user.setAddress("www.mango.com>>>" + i);
            user.setBirthday(new Date());
            userList.add(user);
        }
        return userList;
    }
}

[Run result]

Note: You may not display the same results as I do, but the content is certainly the same. If you need to display it in my format, you need to install a plug-in JSONView to your browser, which is designed specifically for json format, because the complex json format is not easy to read.

4. Expansion

Here's a simple use of json in SpringBoot, and I'll expand on json a little more.

1. If you encounter a date type attribute in a bean, what should json do with the date format?

[First option] We can add the @JsonFormat(pattern = "yyyy-MM-dd") comment to this property, coded as follows:

package com.mango.json.bean;

import com.fasterxml.jackson.annotation.JsonFormat;

import java.util.Date;

public class User {

    private Integer id;
    private String username;
    private String address;
    
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date birthday;

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

If there are many date type attributes in the bean, this is not appropriate, so you can use the second option below.

[Second method] Everyone knows that json can't live without ObjectMapper, because the conversion between json format and objects in java is through the ObjectMapper class, so friends who want to do more research can see the source code of this class.

I mentioned SpringBoot Automation Configuration json above, and I also gave you the path to the json source code so you can see if you see the following code, which is the core of it.

    @Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(ObjectMapper.class)
	@ConditionalOnBean(ObjectMapper.class)
	@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
			havingValue = "jackson", matchIfMissing = true)
	static class MappingJackson2HttpMessageConverterConfiguration {

		@Bean
		@ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class,
				ignoredType = {
						"org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter",
						"org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter" })
		MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
			return new MappingJackson2HttpMessageConverter(objectMapper);
		}

	}

The mappingJackson2HttpMessageConverter method is what we need to use, which SpringBoot provides for us by default. If we don't override this method, it will work by default and vice versa.In other words, we can use json regardless of whether we rewrite the method or not.

Now that we have what we need, we'll rewrite this method with the following code:

[WebMvcConfig.java]

package com.mango.json.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import java.text.SimpleDateFormat;

@Configuration
public class WebMvcConfig {

    @Bean
    MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        converter.setObjectMapper(objectMapper);
        return converter;
    }
}

Note: After overriding this method, we can comment out the comment @JsonFormat(pattern = yyyy-MM-dd) in the bean and restart the project.(If you're interested, debug to see if the default method works.)

In fact, you can see from the code above that we really set the date format by manipulating ObjectMapper, so can this rewritten code be streamlined?The answer is: Of course.

A interested friend must have noticed that there is an ObjectMapper in the mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) method provided by SpringBoot by default above. As to where this ObjectMapper came from, you can see the source code in the following path:

[JacksonAutoConfiguration.java Source Path]

org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration

[The source code is as follows]

@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
	static class JacksonObjectMapperConfiguration {

		@Bean
		@Primary
		@ConditionalOnMissingBean
		ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
			return builder.createXmlMapper(false).build();
		}

	}

Yes, in fact, the ObjectMapper in the mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) method is the ObjectMapper returned by the jacksonObjectMapper method in the source code above. You can debug it to see if it is correct.

Say nothing more. I'll rewrite ObjectMapper below, with the following code:

[WebMvcConfig.java]

package com.mango.json.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import java.text.SimpleDateFormat;

@Configuration
public class WebMvcConfig {

    /*@Bean
    MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        converter.setObjectMapper(objectMapper);
        return converter;
    }*/

    @Bean
    ObjectMapper objectMapper(){
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        return objectMapper;
    }
}

[Run result]

Note: Because the results of the above two methods are the same, only one result graph will be shown.

9 original articles published, 0 praised, 93 visits
Private letter follow

Topics: JSON Java SpringBoot Javascript