报错信息:Could not write JSON: (was java.lang.NullPointerException)
一、问题描述
当查询接口数据封装到统一返回值类中返回到前端时,后台一切正常运行的,但是前台响应会报500错误。
前台响应数据:
{
“timestamp”: “2023-10-12T02:56:32.537+00:00”,
“status”: 500,
“error”: “Internal Server Error”,
“path”: “/query/myTodoTaskList/initiator_LCJ”
}
在后台日志中可以查看到到报错原因:
后台日志
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: cn.zhidasifang.camundaproject.utils.R[“resultObj”]->java.util.ArrayList[0]->org.camunda.bpm.engine.impl.persistence.entity.TaskEntity[“execution”])]
- 统一返回值类代码
public class R extends JSONObject {
/**
*@Description--请求操作成功!!
*@Param [msg, resultObj]
*@return cn.zhidasifang.camundaproject.utils.R
*/
public static R ok(String msg,Object resultObj){
R jsonObject = new R();
jsonObject.put("result","ok");
jsonObject.put("msg",msg);
jsonObject.put("resultObj",resultObj);
return jsonObject;
}
}
- Controller控制层代码
@GetMapping("/myTodoTaskList/{userId}")
public JSONObject queryMyTaskList(@PathVariable(name = "userId")String userId){
List<Task> tasks = null;
try {
tasks = camundaQueryService.queryMyTodoTask(userId);
} catch (Exception e) {
LogUtils.writeErrorException(logger,"查询我的代办失败",e);
//return R.error("查询代办失败",e.getMessage());
}
/*
【新版统一返回参数没数据--原因待查明!!!】
System.out.println("tasks.toString() = " + tasks.toString());
ApiResult<List<Task>> apiResult = ApiResult.success("查询代办成功", tasks);
System.out.println("resultUtils.toString() = " + apiResult.toString());
System.out.println("resultUtils = " + apiResult);
return apiResult;
*/
/**
* Task只是个接口,直接返回会有序列化问题,但是可能和camunda版本也有关系
* */
return R.ok("查询代办成功",tasks);
}
}
*注:注意其中的List<Task> 这个返回值数据!是直接赋值到我们的统一返回参数中的。(后续的报错原因就是由此引起的)
该Task是项目中用到的 import org.camunda.bpm.engine.task.Task; 是Camunda开源框架中的一个接口,其下也有对应的实现类。当我们使用其他开源或者已经封装好的对象,生成返回数据时,可能也会出现类似的错误存在。
- Service服务层代码
@Override
public List<Task> queryMyTodoTask(String userId) {
List<Task> tasks;
try {
tasks = taskService.createTaskQuery()
.taskAssignee(userId)
.list();
} catch (Exception e) {
throw new RuntimeException(e);
}
return tasks;
}
}
这里服务层实现类对应的数据接口,返回的数据就是Task类型的数据。
二、报错原因
当我们直接使用 外部jar封装好的对象 作为 响应结果返回到前端时,就可能出现这种错误情况的出现。因为在外部封装的类对象中,指不定有的属性会出现 Null 的情况,当我们直接将这些对象进行直接返回时,会有序列化问题,难免就会出现在转换为 JSON 数据类型时出现 空指针异常 NullPointerException。
所有在进行响应数据的返回时,如果直接返回这些数据就可能会出现该异常,我们需要自定义的封装一个属性值不存在null的对象来保存这些需要返回的数据。
三、解决思路
首先我们需要自定义一个实体类对象,然后将查询到的数据中需要的保存到我们自定义的实体类中在进行数据的返回。
- 自定义实体类
package cn.zhidasifang.camundaproject.camundaProcessFlow.entity;
import lombok.Data;
import java.io.Serializable;
/**
* @Description: 任务结点对象
* @ClassName: TaskVo
* @Date: 2023-10-12 上午 10:19
*/
@Data
public class TaskVo implements Serializable {
private String id;
protected String name;
private String processInstanceId;
}
- 然后将查询到的数据封装到我们自定义的类中,然后在将自己封装的数据返回。
@Override
public List<TaskVo> queryMyTodoTask(String userId) {
List<Task> tasks;
try {
tasks = taskService.createTaskQuery()
.taskAssignee(userId)
.list();
} catch (Exception e) {
throw new RuntimeException(e);
}
/**
* Task只是个接口,直接返回会有序列化问题,但是可能和camunda版本也有关系!!!!!!!
* */
List<TaskVo> taskVos = null;
/** hutool工具类判断集合是否为null */
if (CollUtil.isNotEmpty(tasks)) {
taskVos = new ArrayList<>();
TaskVo taskVo= null;
for (Task task : tasks) {
/** BeanUtils组件可以实现对象属性的拷贝,copyProperty 实现对象属性的拷贝 */
BeanUtils.copyProperties(task,taskVo=new TaskVo());
taskVos.add(taskVo);
}
}
return taskVos;
}
新的Service层中 将之前查询到的Task类型存储到了我们自定义的 TaskVo 类对象中。
CollUtil :集合相关工具类,此工具方法针对Collection及其实现类封装的工具。
BeanUtils:JavaBeans的静态便利方法:用于实例化bean,检查bean属性类型,复制bean属性等。
同时要注意我们返回参数中的泛型要随之变化!!
- 更新后的Controller控制层
@GetMapping("/myTodoTaskList/{userId}")
public JSONObject queryMyTaskList(@PathVariable(name = "userId")String userId){
List<TaskVo> tasks = null;
try {
tasks = camundaQueryService.queryMyTodoTask(userId);
} catch (Exception e) {
LogUtils.writeErrorException(logger,"查询我的代办失败",e);
//return R.error("查询代办失败",e.getMessage());
}
/*
【新版统一返回参数没数据--原因待查明!!!】
System.out.println("tasks.toString() = " + tasks.toString());
ApiResult<List<Task>> apiResult = ApiResult.success("查询代办成功", tasks);
System.out.println("resultUtils.toString() = " + apiResult.toString());
System.out.println("resultUtils = " + apiResult);
return apiResult;
*/
/**
* Task只是个接口,直接返回会有序列化问题,但是可能和camunda版本也有关系
* */
return R.ok("查询代办成功",tasks);
}
四、修改后访问结果
封装了自定义类后,就不在出现NullPointerExeption空指针异常了(序列化问题)。
{
"result": "ok",
"msg": "查询代办成功",
"resultObj": [
{
"id": "446b1362-680f-11ee-bf11-44af2863f632",
"name": "发起人",
"processInstanceId": "446965a8-680f-11ee-bf11-44af2863f632"
}
]
}