SpringBoot 对Json的配置
jackson 中对null的处理(spring boot 默认)
在实际项目中,我们难免会遇到一些 null 值出现,我们转 json 时,是不希望有这些 null 出现的,比如
我们期望所有的 null 在转 json 时都变成 “” 这种空字符串,那怎么做呢?在 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 objectMapper = builder.createXmlMapper(false).build();
objectMapper.getSerializerProvider().setNullValueSerializer(new
JsonSerializer<Object>() {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString("");
}
});
return objectMapper;
}
}
使用阿里巴巴FastJson的设置
关于 fastJson 和 jackson 的对比,网上有很多资料可以查看,主要是根据自己实际项目情况来选择合适
的框架。从扩展上来看,fastJson 没有 jackson 灵活,从速度或者上手难度来看,fastJson 可以考虑,
我们项目中目前使用的是阿里的 fastJson,挺方便的。
fastJson依赖导入
使用 fastJson 需要导入依赖,本次使用 1.2.35 版本,依赖如下:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.35</version>
</dependency>
使用 fastJson 处理 nul
使用 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.WebMvcConfigurationSupport;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class fastJsonConfig 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);
}
}
返回结构(重要)
自定义返回结构类型
由于封装的 json 数据的类型不确定,所以在定义统一的 json 结构时,我们需要用到泛型。统一的 json
结构中属性包括数据、状态码、提示信息即可,构造方法可以根据实际业务需求做相应的添加即可,一
般来说,应该有默认的返回结构,也应该有用户指定的返回结构。如下:
import cn.hutool.core.exceptions.ValidateException;
import com.common.enums.ResultCodeEnum;
import com.common.enums.ResultStatusEnum;
//这个代码没有引入,可以将最后一个删除
import com.common.exceptions.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.net.ConnectException;
import java.sql.SQLException;
/**
* 统一定义ResponseBody 返回结构
*
*/
@Data
@Slf4j
public class ResultModel implements Serializable {
private static final long serialVersionUID = 1071681926787951549L;
/**
* 响应业务状态
*/
private Integer status;
/**
* 业务响应码, 根据业务响应码反映异常错误类型
*/
private Integer code;
/**
* 响应消息
*/
private String msg;
/**
* 异常详细信息
*/
private String detailMessage;
/**
* 响应中的数据
*/
private Object data;
/**
* 是否请求成功
*/
private boolean success;
/**
* 分页查询时有效
*/
private long total;
private String token;
protected ResultModel(Integer status, String msg, Object data, boolean isSuccess) {
this.status = status;
this.msg = msg;
this.data = data;
this.success = isSuccess;
}
public static ResultModel ok() {
return new ResultModel(ResultCodeEnum.SUCCESS, ResultStatusEnum.OK, ResultStatusEnum.OK.getMsg(), null, true);
}
public static ResultModel ok(Object data) {
return new ResultModel(ResultCodeEnum.SUCCESS, ResultStatusEnum.OK, ResultStatusEnum.OK.getMsg(), data, true);
}
public static ResultModel ok(Object data, String msg) {
return new ResultModel(ResultCodeEnum.SUCCESS, ResultStatusEnum.OK, msg, data, true);
}
public static ResultModel ok(Object data, String msg, Integer status) {
ResultStatusEnum statusEnum = ResultStatusEnum.getByCode(status);
return new ResultModel(ResultCodeEnum.SUCCESS, statusEnum, msg, data, true);
}
public static ResultModel ok(Object data, Integer status) {
ResultStatusEnum statusEnum = ResultStatusEnum.getByCode(status);
return new ResultModel(ResultCodeEnum.SUCCESS, statusEnum, statusEnum.getMsg(), data, true);
}
public static ResultModel fail() {
return new ResultModel(ResultCodeEnum.ERROR, ResultStatusEnum.INTERNAL_SERVER_ERROR,
ResultStatusEnum.INTERNAL_SERVER_ERROR.getMsg(), null, false);
}
public static ResultModel fail(String msg) {
return fail(null, msg);
}
public static ResultModel fail(Throwable throwable) {
return new ResultModel(throwable);
}
public static ResultModel fail(Object data, String msg) {
return new ResultModel(ResultCodeEnum.ERROR, ResultStatusEnum.INTERNAL_SERVER_ERROR, msg, data, false);
}
public static ResultModel fail(Object data, String msg, Integer status) {
ResultStatusEnum statusEnum = ResultStatusEnum.getByCode(status);
return new ResultModel(ResultCodeEnum.ERROR, statusEnum, msg, data, false);
}
public static ResultModel fail(Object data, Integer status) {
ResultStatusEnum statusEnum = ResultStatusEnum.getByCode(status);
return new ResultModel(ResultCodeEnum.ERROR, statusEnum, statusEnum.getMsg(), data, false);
}
public static ResultModel pageOk(Object data, long total) {
ResultModel model = new ResultModel(ResultCodeEnum.SUCCESS, ResultStatusEnum.OK, ResultStatusEnum.OK.getMsg(),
data, true);
model.setTotal(total);
return model;
}
public static ResultModel format(ResultStatusEnum statusEnum) {
boolean isSuccess = true;
ResultCodeEnum codeEnum = ResultCodeEnum.SUCCESS;
switch (statusEnum) {
case INVALID_REQUEST:
case UNAUTHORIZED:
case FORBIDDEN:
case NOT_FOUND:
case INTERNAL_SERVER_ERROR:
codeEnum = ResultCodeEnum.ERROR;
isSuccess = false;
break;
default:
break;
}
return new ResultModel(codeEnum, statusEnum,
statusEnum.getMsg(), null, isSuccess);
}
public static ResultModel format(ResultStatusEnum statusEnum, String msg, Object data) {
boolean isSuccess = true;
ResultCodeEnum codeEnum = ResultCodeEnum.SUCCESS;
switch (statusEnum) {
case INVALID_REQUEST:
case UNAUTHORIZED:
case FORBIDDEN:
case NOT_FOUND:
case INTERNAL_SERVER_ERROR:
codeEnum = ResultCodeEnum.ERROR;
isSuccess = false;
break;
default:
break;
}
return new ResultModel(codeEnum, statusEnum,
msg, data, isSuccess);
}
private ResultModel(ResultCodeEnum codeEnum, ResultStatusEnum statusEnum, String msg, Object data, boolean isSuccess) {
this.status = statusEnum.getCode();
this.code = codeEnum.getCode();
this.msg = msg;
this.data = data;
this.success = isSuccess;
this.total = 0;
}
public ResultModel(Throwable throwable) {
this.success = false;
log.error("系统错误,", throwable);
this.status = 500;
// 获取详细信息
StringWriter stringWriter= new StringWriter();
PrintWriter writer= new PrintWriter(stringWriter);
throwable.printStackTrace(writer);
StringBuffer buffer= stringWriter.getBuffer();
this.detailMessage = buffer.toString();
if (throwable instanceof NullPointerException) {
this.code = 1001;
this.msg = "空指针异常";
} else if (throwable instanceof ClassCastException) {
this.code = 1002;
this.msg = "类型强制转换异常";
} else if (throwable instanceof ConnectException) {
this.code = 1003;
this.msg = "链接失败";
} else if (throwable instanceof IllegalArgumentException) {
this.code = 1004;
this.msg = "传递非法参数异常";
} else if (throwable instanceof IndexOutOfBoundsException) {
this.code = 1006;
this.msg = "下标越界异常";
} else if (throwable instanceof SecurityException) {
this.code = 1007;
this.msg = "安全异常";
} else if (throwable instanceof SQLException) {
this.code = 1008;
this.msg = "数据库错误";
} else if (throwable instanceof ArithmeticException) {
this.code = 1009;
this.msg = "算术运算异常";
} else if (throwable instanceof ValidationException || throwable instanceof ValidateException) {
this.code = 1010;
this.msg = "参数异常," + throwable.getMessage();
} else if (throwable instanceof BusinessException) {
this.code = 500;
this.msg = throwable.getMessage();
} else if (throwable instanceof UnauthorizedException) {
this.code = 401;
this.msg = "权限认证异常";
} else if (throwable instanceof ElasticException) {
this.code = 1011;
this.msg = "Elastic异常";
} else if (throwable instanceof RRException) {
this.code = 1012;
this.msg = "业务异常";
} else if (throwable instanceof RuntimeException) {
this.code = 1013;
this.msg = "运行时异常";
} else if (throwable instanceof Exception) {
this.code = 9999;
this.msg = "服务器错误";
}
}
}
/**
* 请求资源状态码枚举
*
*/
public enum ResultCodeEnum {
/**
* 成功 0
*/
SUCCESS(0, "请求成功"),
/**
* 失败 1
*/
ERROR(1, "请求失败");
private Integer code;
private String msg;
ResultCodeEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
package com.dhcc.mrp.common.enums;
/**
* HTTP状态码
*
*/
public enum ResultStatusEnum {
/**
* 200 请求成功
*/
OK(200, "OK", "请求成功!"),
/**
* 201 创建或者修改数据成功
*/
CREATED(201, "CREATED", "资源创建成功!"),
/**
* 204 删除数据成功
*/
NO_CONTENT(204, "NO CONTENT", "服务器成功处理了请求,但不需要返回任何实体内容!"),
/**
* 400 请求有错误
*/
INVALID_REQUEST(400, "INVALID REQUEST", "请求失败,具体查看返回业务状态码与对应消息!"),
/**
* 401 没有权限
*/
UNAUTHORIZED(401, "Unauthorized", "请求失败,未经过身份认证!"),
/**
* 403 用户访问被禁止(已经授权)
*/
FORBIDDEN(403, "Forbidden", "用户访问被禁止!"),
/**
* 404 请求不存在
*/
NOT_FOUND(404, "NOT FOUND", "请求不存在!"),
/**
* 405 请求方法不支持
*/
NOT_SUPPORT(405, "Not Support", "请求方法不支持!"),
/**
* 500 服务器发生错误
*/
INTERNAL_SERVER_ERROR(500, "INTERNAL SERVER ERROR", "服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理!");
private Integer code;
private String name;
private String msg;
ResultStatusEnum(Integer code, String name, String msg) {
this.code = code;
this.name = name;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
public static ResultStatusEnum getByCode(Integer code) {
if (code == null) {
// 默认返回200
return ResultStatusEnum.OK;
}
ResultStatusEnum[] types = ResultStatusEnum.values();
for (ResultStatusEnum type : types) {
if (type.code.equals(code)) {
return type;
}
}
return ResultStatusEnum.OK;
}
}