同步操作将从 flatfish/Java-Review 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
有很多朋友习惯于使用阿里巴巴的 fastJson 来做项目中 json 转换的相关工作,目前我们项目中使用的就是阿里的 fastJson,那么 jackson 和 fastJson 有哪些区别呢?根据网上公开的资料比较得到下表。
选项 | fastJson | jackson |
---|---|---|
上手难易程度 | 容易 | 中等 |
高级特性支持 | 中等 | 丰富 |
官方文档、Example支持 | 中文 | 英文 |
处理json速度 | 略快 | 快 |
关于 fastJson 和 jackson 的对比,网上有很多资料可以查看,主要是根据自己实际项目情况来选择合适的框架。从扩展上来看,fastJson 没有 jackson 灵活,从速度或者上手难度来看,fastJson 可以考虑。
在项目开发中,接口与接口之间,前后端之间数据的传输都使用 Json 格式,在 Spring Boot 中,接口返回 Json 格式的数据很简单,在 Controller 中使用@RestController
注解即可返回 Json 格式的数据,@RestController
也是 Spring Boot 新增的一个注解,我们点进去看一下该注解都包含了哪些东西。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
* @since 4.0.1
*/
@AliasFor(annotation = Controller.class)
String value() default "";
}
可以看出, @RestController
注解包含了原来的 @Controller
和 @ResponseBody
注解,使用过 Spring 的朋友对 @Controller
注解已经非常了解了,这里不再赘述, @ResponseBody
注解是将返回的数据结构转换为 Json 格式。所以在默认情况下,使用了 @RestController
注解即可将返回的数据结构转换成 Json 格式,Spring Boot 中默认使用的 Json 解析技术框架是 jackson。我们点开 pom.xml 中的 spring-boot-starter-web
依赖,可以看到一个 spring-boot-starter-json
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.3.0.RELEASE</version>
<scope>compile</scope>
</dependency>
Spring Boot 中对依赖都做了很好的封装,可以看到很多 spring-boot-starter-xxx
系列的依赖,这是 Spring Boot 的特点之一,不需要人为去引入很多相关的依赖了,starter-xxx 系列直接都包含了所必要的依赖,所以我们再次点进去上面这个 spring-boot-starter-json
依赖,可以看到:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
到此为止,我们知道了 Spring Boot 中默认使用的 json 解析框架是 jackson。下面我们看一下默认的 jackson 框架对常用数据类型的转 Json 处理。
为了测试,这里创建一个User实体类
其中@Date是Lombok注解 用来生成get、set、toString等方法 注意,在使用Lombok注解的时候,需要手动添加无参构造方法, 否则会报错无法创建 bean
@Component
@Data
public class User {
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
创建一个UserController
用来测试返回的结果分别为User
对象 List<User>
对象 Map<User,Object>
对象
@RestController
@RequestMapping("/jackson")
public class UserController {
@RequestMapping("/user")
public User user() {
return new User("icanci", "hashmap");
}
@RequestMapping("/list")
public List<User> list() {
ArrayList<User> users = new ArrayList<>();
users.add(new User("icanci", "hashmap"));
users.add(new User("icanci1", "hashmap1"));
users.add(new User("icanci2", "hashmap2"));
users.add(new User("icanci3", "hashmap3"));
users.add(new User("icanci4", "hashmap4"));
return users;
}
@RequestMapping("/map")
public Map<User, Object> map() {
HashMap<User, Object> map = new HashMap<>();
map.put(new User("icanci", "hashmap"), "1");
map.put(new User("icanci1", "hashmap1"), "2");
map.put(new User("icanci2", "hashmap2"), "3");
map.put(new User("icanci3", "hashmap3"), "4");
map.put(new User("icanci4", "hashmap4"), "5");
return map;
}
}
在浏览器输入http://localhost:8080/jackson/user
返回数据如下
{
"username": "icanci",
"password": "hashmap"
}
在浏览器输入http://localhost:8080/jackson/list
返回数据如下
[
{
"username": "icanci",
"password": "hashmap"
},
{
"username": "icanci1",
"password": "hashmap1"
},
{
"username": "icanci2",
"password": "hashmap2"
},
{
"username": "icanci3",
"password": "hashmap3"
},
{
"username": "icanci4",
"password": "hashmap4"
}
]
在浏览器输入 http://localhost:8080/jackson/map
返回的数据如下
{
"User(username=icanci2, password=hashmap2)": "3",
"User(username=icanci3, password=hashmap3)": "4",
"User(username=icanci4, password=hashmap4)": "5",
"User(username=icanci, password=hashmap)": "1",
"User(username=icanci1, password=hashmap1)": "2"
}
由上述结果即可看出,无论浏览器输入怎样的请求,都可以返回对应的 Json 数据
在实际项目中,我们难免会遇到一些 null 值出现,我们转 json 时,是不希望有这些 null 出现的,比如我们期望所有的 null 在转 json 时都变成 "" 这种空字符串,那怎么做呢?
# 修改之后返回的JSON数据
[
{
"username": "icanci",
"password": "hashmap"
},
{
"username": "icanci1",
"password": "hashmap1"
},
{
"username": "icanci2",
"password": "hashmap2"
},
{
"username": "icanci3",
"password": "hashmap3"
},
null
]
在 Spring Boot 中,我们做一下配置即可,新建一个 jackson 的配置类:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.io.IOException;
@Configuration
public class JacksonConfig {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder){
ObjectMapper build = builder.createXmlMapper(false).build();
build.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString("");
}
});
return build;
}
}
这样,我们再修改返回map的接口再测试一下
@RequestMapping("/map")
public Map<User, Object> map() {
HashMap<User, Object> map = new HashMap<>();
map.put(new User("icanci", "hashmap"), null);
map.put(new User(), "2");
map.put(new User("icanci2", "hashmap2"), "3");
map.put(new User("icanci3", "hashmap3"), null);
map.put(new User("icanci4", "hashmap4"), "5");
return map;
}
重启项目,访问地址http://localhost:8080/jackson/map
返回结果如下
{
"User(username=icanci2, password=hashmap2)": "3",
"User(username=icanci3, password=hashmap3)": "",
"User(username=icanci4, password=hashmap4)": "5",
"User(username=icanci, password=hashmap)": "",
"User(username=null, password=null)": "2"
}
前段时间,阿里巴巴的 fastjson 出现了重大Bug,一时间传的沸沸扬扬,但是再中国它依旧很火。
使用 fastJson 需要导入依赖,本教程使用 1.2.35 版本,依赖如下:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.35</version>
</dependency>
使用 fastJson 时,对 null 的处理和 jackson 有些不同,需要继承 WebMvcConfigurationSupport
类,然后覆盖 configureMessageConverters
方法,在方法中,我们可以选择对要实现 null 转换的场景,配置好即可。如下:
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class MyFastJsonConfig extends WebMvcConfigurationSupport {
/**
* 使用阿里 FastJson 作为JSON MessageConverter
*
* @param converters
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(
// 保留map空的字段
SerializerFeature.WriteMapNullValue,
// 将String类型的null转成""
SerializerFeature.WriteNullStringAsEmpty,
// 将Number类型的null转成0
SerializerFeature.WriteNullNumberAsZero,
// 将List类型的null转成[]
SerializerFeature.WriteNullListAsEmpty,
// 将Boolean类型的null转成false
SerializerFeature.WriteNullBooleanAsFalse,
// 避免循环引用
SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
converter.setDefaultCharset(Charset.forName("UTF-8"));
List<MediaType> mediaTypeList = new ArrayList<>();
// 解决中文乱码问题,相当于在Controller上的@RequestMapping中加了个属性produces = "application/json"
mediaTypeList.add(MediaType.APPLICATION_JSON);
converter.setSupportedMediaTypes(mediaTypeList);
converters.add(converter);
}
}
注意 彩坑记录:这里进行类命名的时候,不可以使用 FastJsonConfig
因为 FastJsonConfig
是阿里巴巴 FastJson
的一个配置类,我把自己的命名为这个,导致方法找不出来,所以我修改命名为MyFastJsonConfig
即可
但是我本地测试,发现界面依旧显示的是null,而非是空,问题暂未解决
以上是 Spring Boot 返回 json 的几个代表的例子,但是在实际项目中,除了要封装数据之外,我们往往需要在返回的 json 中添加一些其他信息,比如返回一些状态码 code ,返回一些 msg 给调用者,这样调用者可以根据 code 或者 msg 做一些逻辑判断。所以在实际项目中,我们需要封装一个统一的 json 返回结构存储返回信息。
由于封装的 json 数据的类型不确定,所以在定义统一的 json 结构时,我们需要用到泛型。统一的 json 结构中属性包括数据、状态码、提示信息即可,构造方法可以根据实际业务需求做相应的添加即可,一般来说,应该有默认的返回结构,也应该有用户指定的返回结构。如下:
@Component
@Getter
@Setter
@ToString
public class JsonResult<T> {
private T data;
private String code;
private String msg;
/**
* 若没有数据返回,默认状态码为0,提示信息为:操作成功!
*/
public JsonResult() {
this.code = "0";
this.msg = "操作成功!";
}
/**
* 若没有数据返回,可以人为指定状态码和提示信息
*
* @param code
* @param msg
*/
public JsonResult(String code, String msg) {
this.code = code;
this.msg = msg;
}
/**
* 有数据返回时,状态码为0,默认提示信息为:操作成功!
*
* @param data
*/
public JsonResult(T data) {
this.data = data;
this.code = "0";
this.msg = "操作成功!";
}
/**
* 有数据返回,状态码为0,人为指定提示信息
*
* @param data
* @param msg
*/
public JsonResult(T data, String msg) {
this.data = data;
this.code = "0";
this.msg = msg;
}
/**
* 有数据返回,状态码人为设置,人为指定提示信息
*
* @param data
* @param msg
* @param code
*/
public JsonResult(T data, String msg,String code) {
this.data = data;
this.code = code;
this.msg = msg;
}
}
由于 JsonResult 使用了泛型,所以所有的返回值类型都可以使用该统一结构,在具体的场景将泛型替换成具体的数据类型即可,非常方便,也便于维护。在实际项目中,还可以继续封装,比如状态码和提示信息可以定义一个枚举类型,以后我们只需要维护这个枚举类型中的数据即可(在本教程中就不展开了)。根据以上的 JsonResult,我们改写一下 Controller,如下:
@RestController
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/user")
public JsonResult<User> user() {
return new JsonResult<>(new User("icanci", "hashmap"),"success","200");
}
@RequestMapping("/list")
public JsonResult< List<User>> list() {
ArrayList<User> users = new ArrayList<>();
users.add(new User("icanci", "hashmap"));
users.add(new User("icanci1", "hashmap1"));
users.add(new User("icanci2", "hashmap2"));
users.add(new User("icanci3", "hashmap3"));
users.add(new User("icanci4", "hashmap4"));
users.add(null);
return new JsonResult<>(users,"获取用户列表成功");
}
@RequestMapping("/map")
public JsonResult<Map<User, Object>> map() {
HashMap<User, Object> map = new HashMap<>();
map.put(new User("icanci", "hashmap"), null);
map.put(new User(), "2");
map.put(new User("icanci2", "hashmap2"), "3");
map.put(new User("icanci3", "hashmap3"), null);
map.put(new User("icanci4", "hashmap4"), "5");
return new JsonResult<>(map);
}
}
浏览器输入http://localhost:8080/hello/user
返回结果如下
{
"data": {
"username": "icanci",
"password": "hashmap"
},
"code": "200",
"msg": "success"
}
浏览器输入http://localhost:8080/hello/list
返回结果如下
{
"data": [
{
"username": "icanci",
"password": "hashmap"
},
{
"username": "icanci1",
"password": "hashmap1"
},
{
"username": "icanci2",
"password": "hashmap2"
},
{
"username": "icanci3",
"password": "hashmap3"
},
{
"username": "icanci4",
"password": "hashmap4"
},
""
],
"code": "0",
"msg": "获取用户列表成功"
}
浏览器输入http://localhost:8080/hello/map
返回结果如下
{
"data": {
"User(username=icanci2, password=hashmap2)": "3",
"User(username=icanci3, password=hashmap3)": "",
"User(username=icanci4, password=hashmap4)": "5",
"User(username=icanci, password=hashmap)": "",
"User(username=null, password=null)": "2"
},
"code": "0",
"msg": "操作成功!"
}
通过封装,我们不但将数据通过 json 传给前端或者其他接口,还带上了状态码和提示信息,这在实际项目场景中应用非常广泛。
本节主要对 Spring Boot 中 json 数据的返回做了详细的分析,从 Spring Boot 默认的 jackson 框架到阿里巴巴的 fastJson 框架,分别对它们的配置做了相应的讲解。另外,结合实际项目情况,总结了实际项目中使用的 json 封装结构体,加入了状态码和提示信息,使得返回的 json 数据信息更加完整。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。