springboot 接口返回json中null转换成空数组或空字符串(递归+反射实现)

简介: 本来想加一个Jackson的配置类修改ObjectMapper实现把null转空字符串或空数组,但是加上之后发现没效果,找不到问题在哪里,不知道是不是我使用@RestControllerAdvice全局返回处理类的问题,所以就自己写了一个工具类方法处理,就在全局返回处理类里面调用

本来想加一个Jackson的配置类修改ObjectMapper实现把null转空字符串或空数组,但是加上之后发现没效果,找不到问题在哪里,不知道是不是我使用@RestControllerAdvice全局返回处理类的问题,所以就自己写了一个工具类方法处理,就在全局返回处理类里面调用


找到配置不生效的问题在哪里了,见springboot中添加Jackson配置类不生效


全局返回处理类是用kotlin写的,用来封装统一响应实体和处理全局异常的,用java也是一样的,语法换成java就行。当然,这不是这篇的博文重点,重点是处理null的方法


package com.gt.gxjhpt.configuration
import cn.dev33.satoken.exception.NotLoginException
import cn.dev33.satoken.exception.NotPermissionException
import cn.dev33.satoken.exception.NotRoleException
import cn.hutool.json.JSONUtil
import com.gt.gxjhpt.entity.RestfulResp
import com.gt.gxjhpt.enumeration.RespCodeEnum
import com.gt.gxjhpt.exception.MyException
import com.gt.gxjhpt.utils.MyUtils
import org.springframework.core.MethodParameter
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.converter.HttpMessageConverter
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServerHttpResponse
import org.springframework.http.server.ServletServerHttpResponse
import org.springframework.validation.BindException
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestControllerAdvice
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice
import javax.validation.ConstraintViolationException
@RestControllerAdvice
class GlobalResponseBodyAdvice : ResponseBodyAdvice<Any> {
    override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean {
        return true
    }
    override fun beforeBodyWrite(
        body: Any?,
        returnType: MethodParameter,
        selectedContentType: MediaType,
        selectedConverterType: Class<out HttpMessageConverter<*>>,
        request: ServerHttpRequest,
        response: ServerHttpResponse
    ): Any? {
        return when (body) {
            is RestfulResp<*> -> body
            is String -> JSONUtil.toJsonStr(RestfulResp<Any>().success(body))
            null -> RestfulResp<Any>().success()
            else -> {
                val httpResponse = response as ServletServerHttpResponse
                if (httpResponse.servletResponse.status.equals(200)) {
                    // 设置null值为空字符串或空数组
                    MyUtils.setNullValue(body)
                    return RestfulResp<Any>().success(body)
                } else {
                    return body
                }
            }
        }
    }
    //其他参数异常
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = [BindException::class])
    fun handleBindException(): RestfulResp<*>? {
        return RestfulResp<Any>().enumResp(RespCodeEnum.PARAM_ERR)
    }
    //parameter异常
    //为了安全,就不将报错信息返回到前端,只返回粗略信息
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = [ConstraintViolationException::class])
    fun handleValidationException(): RestfulResp<*>? {
        return RestfulResp<Any>().enumResp(RespCodeEnum.PARAM_ERR)
    }
    //bean异常
    //为了安全,就不将报错信息返回到前端,只返回粗略信息
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = [MethodArgumentNotValidException::class])
    fun handleMethodArgumentNotValidException(ex: MethodArgumentNotValidException?): RestfulResp<*>? {
        return RestfulResp<Any>().enumResp(RespCodeEnum.PARAM_ERR)
    }
    //我的自定义异常
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(
        MyException::class
    )
    fun myException(e: MyException): RestfulResp<*>? {
        e.printStackTrace()
        return if (e.msg != null) {
            RestfulResp<Any>().error(e.msg, e.code)
        } else if (e.respCodeEnum != null) {
            RestfulResp<Any>().enumResp(e.respCodeEnum)
        } else {
            RestfulResp<Any>().unknown()
        }
    }
    //运行时异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(
        RuntimeException::class
    )
    fun runtimeException(e: RuntimeException): RestfulResp<*>? {
        e.printStackTrace()
        return RestfulResp<Any>().unknown(e.message)
    }
    //空指针异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(
        NullPointerException::class
    )
    fun nullPointerException(e: NullPointerException): RestfulResp<*>? {
        e.printStackTrace()
        return RestfulResp<Any>().unknown("空指针-->>" + e.stackTrace[0].toString())
    }
    //未知异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(
        Exception::class
    )
    fun exception(e: Exception): RestfulResp<*>? {
        e.printStackTrace()
        return RestfulResp<Any>().unknown(e.message)
    }
    // 未提供token
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(value = [NotLoginException::class])
    fun exception(e: NotLoginException): RestfulResp<*>? {
        e.printStackTrace()
        return RestfulResp<Any>().unknown(e.message)
    }
    // 无角色
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(
        NotRoleException::class
    )
    fun exception(e: NotRoleException): RestfulResp<*>? {
        e.printStackTrace()
        return RestfulResp<Any>().unknown(e.message)
    }
    // 无权限
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(
        NotPermissionException::class
    )
    fun exception(e: NotPermissionException): RestfulResp<*>? {
        e.printStackTrace()
        return RestfulResp<Any>().unknown(e.message)
    }
}


处理null的方法,不需要继续递归的类型,要写在第一个else if条件里面,工具类使用hutool


public static void setNullValue(Object body) throws IllegalAccessException, InstantiationException {
        Class<?> aClass = body.getClass();
        if (Collection.class.isAssignableFrom(aClass)) {
            for (Object it : Convert.toList(body)) {
                MyUtils.setNullValue(it);
            }
        } else if (String.class.isAssignableFrom(aClass) || NumberUtil.isNumber(body.toString())
                || BooleanUtil.isBoolean(aClass) || CharUtil.isChar(body) || Date.class.isAssignableFrom(aClass)) {
        } else if (Map.class.isAssignableFrom(aClass)) {
            Map<String, Object> objectMap = Convert.toMap(String.class, Object.class, body);
            objectMap.forEach((k, v) -> {
                try {
                    if (v == null) {
                        v = "";
                    } else {
                        MyUtils.setNullValue(v);
                    }
                } catch (IllegalAccessException | InstantiationException e) {
                    e.printStackTrace();
                }
                objectMap.put(k, v);
            });
        } else { // 自定义响应对象
            List<Field> fields = CollUtil.toList(aClass.getDeclaredFields());
            // 父类属性
            Class<?> superclass = aClass.getSuperclass();
            while (superclass != null && superclass != Object.class) {
                fields.addAll(CollUtil.toList(superclass.getDeclaredFields()));
                superclass = superclass.getSuperclass();
            }
            for (Field field : fields) {
                // 取消权限检查
                field.setAccessible(true);
                Object value = field.get(body);
                if (value == null) {
                    if (Collection.class.isAssignableFrom(field.getType())) {
                        field.set(body, new ArrayList<>());
                    } else if (String.class.isAssignableFrom(field.getType())) {
                        field.set(body, "");
                    }
                } else {
                    MyUtils.setNullValue(value);
                }
            }
        }
    }
相关文章
|
7月前
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——封装统一返回的数据结构
本文介绍了在Spring Boot中封装统一返回的数据结构的方法。通过定义一个泛型类`JsonResult&lt;T&gt;`,包含数据、状态码和提示信息三个属性,满足不同场景下的JSON返回需求。例如,无数据返回时可设置默认状态码&quot;0&quot;和消息&quot;操作成功!&quot;,有数据返回时也可自定义状态码和消息。同时,文章展示了如何在Controller中使用该结构,通过具体示例(如用户信息、列表和Map)说明其灵活性与便捷性。最后总结了Spring Boot中JSON数据返回的配置与实际项目中的应用技巧。
541 0
|
7月前
|
JSON Java fastjson
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——使用 fastJson 处理 null
本文介绍如何使用 fastJson 处理 null 值。与 Jackson 不同,fastJson 需要通过继承 `WebMvcConfigurationSupport` 类并覆盖 `configureMessageConverters` 方法来配置 null 值的处理方式。例如,可将 String 类型的 null 转为 &quot;&quot;,Number 类型的 null 转为 0,避免循环引用等。代码示例展示了具体实现步骤,包括引入相关依赖、设置序列化特性及解决中文乱码问题。
329 0
|
7月前
|
JSON Java fastjson
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——Spring Boot 默认对Json的处理
本文介绍了在Spring Boot中返回Json数据的方法及数据封装技巧。通过使用`@RestController`注解,可以轻松实现接口返回Json格式的数据,默认使用的Json解析框架是Jackson。文章详细讲解了如何处理不同数据类型(如类对象、List、Map)的Json转换,并提供了自定义配置以应对null值问题。此外,还对比了Jackson与阿里巴巴FastJson的特点,以及如何在项目中引入和配置FastJson,解决null值转换和中文乱码等问题。
941 0
|
3月前
|
JSON Java 数据格式
Spring Boot返回Json数据及数据封装
在Spring Boot中,接口间及前后端的数据传输通常使用JSON格式。通过@RestController注解,可轻松实现Controller返回JSON数据。该注解是Spring Boot新增的组合注解,结合了@Controller和@ResponseBody的功能,默认将返回值转换为JSON格式。Spring Boot底层默认采用Jackson作为JSON解析框架,并通过spring-boot-starter-json依赖集成了相关库,包括jackson-databind、jackson-datatype-jdk8等常用模块,简化了开发者对依赖的手动管理。
383 3
|
7月前
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot中的全局异常处理——定义返回的统一 json 结构
本课主要讲解Spring Boot中的全局异常处理方法。在项目开发中,各层操作难免会遇到各种异常,若逐一处理将导致代码耦合度高、维护困难。因此,需将异常处理从业务逻辑中分离,实现统一管理与友好反馈。本文通过定义一个简化的JsonResult类(含状态码code和消息msg),结合全局异常拦截器,展示如何封装并返回标准化的JSON响应,从而提升代码质量和用户体验。
148 0
|
11月前
|
JSON Java 数据格式
springboot中表字段映射中设置JSON格式字段映射
springboot中表字段映射中设置JSON格式字段映射
438 1
|
JSON Java API
解码Spring Boot与JSON的完美融合:提升你的Web开发效率,实战技巧大公开!
【8月更文挑战第29天】Spring Boot作为Java开发的轻量级框架,通过`jackson`库提供了强大的JSON处理功能,简化了Web服务和数据交互的实现。本文通过代码示例介绍如何在Spring Boot中进行JSON序列化和反序列化操作,并展示了处理复杂JSON数据及创建RESTful API的方法,帮助开发者提高效率和应用性能。
611 0
|
27天前
|
机器学习/深度学习 JSON 监控
淘宝拍立淘按图搜索与商品详情API的JSON数据返回详解
通过调用taobao.item.get接口,获取商品标题、价格、销量、SKU、图片、属性、促销信息等全量数据。
|
15天前
|
JSON 缓存 自然语言处理
多语言实时数据微店商品详情API:技术实现与JSON数据解析指南
通过以上技术实现与解析指南,开发者可高效构建支持多语言的实时商品详情系统,满足全球化电商场景需求。
|
29天前
|
JSON API 数据格式
干货满满!淘宝商品详情数据,淘宝API(json数据返回)
淘宝商品详情 API 接口(如 taobao.item.get)的 JSON 数据返回示例如下