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;
    }

}