1.背景
为了适配国产化,需对现有项目进行改造升级,由原来的EJB框架升级为spring-boot。
在升级过程中,发现fastjson2 源码有些小bug,在序列化对象时,对于深度嵌套的对象结构,fastjson2 可能会出现意外的行为,如丢失部分数据或生成不符合预期的 JSON 结构。
故而打算将fastjson全部替换为使用jackson,其中需要注意的是对象的属性过滤,也就是fastjson出现的bug。在jackson工具类中进行了自定义过滤器和方法封装,封装的方法名类似fastjson2,方便对fastjson2进行快速替换,且能实现深度嵌套对象的正确属性过滤。
2.动态过滤对象属性
2.1 网上常见方法
jackson中对象的属性字段过滤有很多种方法,包括注解、自定义序列化器和反序列化器、ObjectMapper 配置、Mixin 注解以及基于视图的过滤等,网上大多是使用Mixin注解,如:
public abstract class UserMixin {
@JsonIgnore
private String password;
}
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(User.class, UserMixin.class);
如果需要过滤User类,需要创建对应的UserMixin类
注:@JsonIgnore:完全忽略指定字段,使其不参与序列化和反序列化,通过 Mixin 注解类将注解应用到现有类上,而无需修改原始类。
2.2 jackson封装的动态过滤对象属性
上文中的Mixin 注解类方法的问题是每个类都要创建一个对应的Mixin类,比较繁琐。
基于jackson中Mixin的强大机制,在jackson封装的工具类中,封装了一个动态过滤的方法,需要增加一个JacksonFilter过滤器类,核心就是jackson中的addMixin是可以给一个类附加上去一个过滤器类的。该方法解决了每次过滤都要创建对应的Mixin 注解类的问题。
2.2.1 封装的jackson工具类
package com.test;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JsonUtil {
public static final ObjectMapper mapper = new ObjectMapper();
static {
// 配置序列化功能,当序列化空对象时不会抛出异常
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
// 设置序列化方式,只序列化非空字段
mapper.setSerializationInclusion(Include.NON_NULL);
// 启用JSON 输出时的缩进格式
mapper.enable(SerializationFeature.INDENT_OUTPUT);
// 配置反序列化功能,忽略JSON中未知的属性
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
/**
* 将 JSON 字符串转换为指定类型的对象
*
* @param jsonString JSON 字符串
* @param clazz 目标对象的 Class 类型
* @param <T> 目标对象的类型
* @return 转换后的对象,如果转换失败则返回 null
*/
public static <T> T parseObject(String jsonString, Class<T> clazz) {
try {
return mapper.readValue(jsonString, clazz);
} catch (IOException e) {
Logger.getLogger(JsonUtil.class.getName()).log(Level.SEVERE, "Failed to parse JSON string to object", e);
return null;
}
}
/**
* 将 JSON 字符串转换为指定类型的对象
*
* @param jsonString JSON 字符串
* @param typeReference 目标对象的 TypeReference 类型
* @param <T> 目标对象的类型
* @return 转换后的对象,如果转换失败则返回 null
*/
public static <T> T parseObject(String jsonString, TypeReference<T> typeReference) {
try {
return mapper.readValue(jsonString, typeReference);
} catch (IOException e) {
Logger.getLogger(JsonUtil.class.getName()).log(Level.SEVERE, "Failed to parse JSON string to object<T>", e);
return null;
}
}
/**
* 将 JSON 字符串转换为 List 对象
*
* @param jsonString JSON 字符串
* @param clazz List 中元素的 Class 类型
* @param <T> List 中元素的类型
* @return 转换后的 List 对象,如果转换失败则返回 null
*/
public static <T> List<T> parseArray(String jsonString, Class<T> clazz) {
try {
return mapper.readValue(jsonString, mapper.getTypeFactory().constructCollectionType(List.class, clazz));
} catch (IOException e) {
Logger.getLogger(JsonUtil.class.getName()).log(Level.SEVERE, "Failed to parse JSON array to list", e);
return null;
}
}
/**
* 将 JSON 字符串转换为 List 对象
*
* @param jsonString JSON 字符串
* @param typeReference 目标对象的 TypeReference 类型
* @param <T> 目标对象的类型
* @return 转换后的 List 对象,如果转换失败则返回 null
*/
public static <T> List<T> parseArray(String jsonString, TypeReference<List<T>> typeReference) {
try {
return mapper.readValue(jsonString, typeReference);
} catch (IOException e) {
Logger.getLogger(JsonUtil.class.getName()).log(Level.SEVERE, "Failed to parse JSON array to list<T>", e);
return null;
}
}
/**
* 将对象转换为 JSON 字符串
*
* @param obj 要转换的对象
* @return JSON 字符串,如果转换失败则返回空字符串
*/
public static String toJSONString(Object obj) {
try {
return mapper.writeValueAsString(obj);
} catch (IOException e) {
Logger.getLogger(JsonUtil.class.getName()).log(Level.SEVERE, "Failed to convert object to JSON string", e);
return "";
}
}
/**
* 将对象转换为JSON字符串,应用自定义过滤器,通过包含或过滤对象属性来控制序列化的过程
*
* @param obj 要转换为JSON字符串的对象,不能为空
* @param includeMap 用于指定要包含(保留)的属性,键是类,值是要包含的属性数组
* @param filterMap 用于指定要过滤(排除)的属性,键是类,值是要过滤的属性数组
* @return 对象的JSON字符串
*/
public static String toJSONString(Object obj, Map<Class<?>, String[]> includeMap, Map<Class<?>, String[]> filterMap) {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
JacksonFilter jacksonFilter = new JacksonFilter();
if (obj == null) {
throw new IllegalArgumentException("Object cannot be null");
}
// 设置过滤配置
if (includeMap != null && !includeMap.isEmpty()) {
for (Map.Entry<Class<?>, String[]> entry : includeMap.entrySet()) {
jacksonFilter.include(entry.getKey(), entry.getValue());
// 应用 Mixin,使 Jackson 使用 JacksonFilter 的过滤逻辑
objectMapper.addMixIn(entry.getKey(), jacksonFilter.getClass());
}
}
if (filterMap != null && !filterMap.isEmpty()) {
for (Map.Entry<Class<?>, String[]> entry : filterMap.entrySet()) {
jacksonFilter.filter(entry.getKey(), entry.getValue());
objectMapper.addMixIn(entry.getKey(), jacksonFilter.getClass());
}
}
// 设置过滤器
objectMapper.setFilterProvider(jacksonFilter);
try {
return objectMapper.writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException("转换json字符失败!", e);
}
}
public static void writeValueToStream(OutputStream os, Object obj) {
try {
mapper.writeValue(os, obj);
} catch (IOException e) {
Logger.getLogger(JsonUtil.class.getName()).log(Level.SEVERE, "Failed to convert object to stream", e);
}
}
public static byte[] writeValueAsBytes(Object obj) {
try {
return mapper.writeValueAsBytes(obj);
} catch (IOException e) {
Logger.getLogger(JsonUtil.class.getName()).log(Level.SEVERE, "Failed to convert object to byte", e);
return new byte[0];
}
}
public static JsonNode getPropertyNode(JsonNode node, String field) {
JsonNode result = null;
if ((node != null) && (!node.isNull())) {
if (field.contains(".")) {
int index = field.indexOf(".") + 1;
String part = field.substring(0, index - 1);
String nextField = field.substring(index);
result = getPropertyNode(node.get(part), nextField);
} else {
result = node.get(field);
}
}
return result;
}
public static String getText(JsonNode node, String field, String defaultValue) {
String result = defaultValue;
if (!node.isNull()) {
JsonNode fieldNode = getPropertyNode(node, field);
if ((fieldNode != null) && (!fieldNode.isNull())) {
result = fieldNode.asText();
}
}
return result;
}
public static ObjectNode createObjectNode() {
return mapper.createObjectNode();
}
/**
* 将给定的对象转换为ObjectNode对象
* 用于序列化一个Java对象为JSON对象,并将其封装在ObjectNode中
*
* @param pojo 要转换为ObjectNode的Java对象
* @return 转换后的ObjectNode对象,如果发生错误则返回null
*/
public static ObjectNode createObjectNode(Object pojo) {
if (pojo == null) {
throw new IllegalArgumentException("Pojo can not be null.");
} else {
try {
ObjectNode objectNode = createObjectNode();
JsonParser jsonParser = mapper.getFactory().createParser(writeValueAsBytes(pojo));
JsonNode jsonNode = (JsonNode) jsonParser.readValueAsTree();
if (!jsonNode.isObject()) {
throw new RuntimeException("JsonNode [" + jsonNode + "] is not a object.");
} else {
objectNode.setAll((ObjectNode) jsonNode);
return objectNode;
}
} catch (IOException e) {
Logger.getLogger(JsonUtil.class.getName()).log(Level.SEVERE, "Failed to convert JsonNode to objectNode", e);
return null;
}
}
}
public static ArrayNode createArrayNode() {
return mapper.createArrayNode();
}
public static JsonNode toJsonNode(Object obj) {
try {
return mapper.valueToTree(obj);
} catch (Exception e) {
Logger.getLogger(JsonUtil.class.getName()).log(Level.SEVERE, "Failed to convert object to JsonNode", e);
return null;
}
}
}
2.2.1.1 工具类中createArrayNode方法说明:
其中createArrayNode是对应fastjson中的JSONArray,即用来将
JSONArray arry= new JSONArray();
替换为:
ArrayNode arry= JsonUtil.createArrayNode();
2.2.1.2 工具类中createObjectNode方法说明:
其中createObjectNode是对应fastjson中的JSONObject,即用来将
JSONObject jsonObject = new JSONObject();
替换为:
ObjectNode arry= JsonUtil.createObjectNode();
2.2.1.3 工具类中toJsonNode方法说明:
在创建ObjectNode时,ObjectNode 本身不能直接存放 Java 对象,只能存放基本类型的值(如字符串、数字等)或其它 JsonNode 类型的对象,因此需要先将 Java 对象转换为 Jackson 的 JsonNode 类型,如:
TbFile tbFile = new TbFile();
tbFile.setId("1111");
tbFile.setName("文件");
ObjectNode briefNode = JsonUtil.createObjectNode();
briefNode.put("title","标题");
//存放对象用.set
briefNode.set("file", JsonUtil.toJsonNode(tbFile));
注:ObjectNode对应fastjson中的JSONObject
2.2.2 JacksonFilter过滤器类
package com.test;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.PropertyFilter;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import java.util.*;
@SuppressWarnings("deprecation")
@JsonFilter("JacksonFilter")
public class JacksonFilter extends FilterProvider {
Map<Class<?>, Set<String>> includeMap = new HashMap<>();
Map<Class<?>, Set<String>> filterMap = new HashMap<>();
public void include(Class<?> type, String[] fields) {
addToMap(includeMap, type, fields);
}
public void filter(Class<?> type, String[] fields) {
addToMap(filterMap, type, fields);
}
private void addToMap(Map<Class<?>, Set<String>> map, Class<?> type, String[] fields) {
Set<String> filedSet=new HashSet<>();
if(fields!=null && fields.length>0){
filedSet.addAll(Arrays.asList(fields));
}
map.put(type, filedSet);
}
@Override
public BeanPropertyFilter findFilter(Object filterId) {
throw new UnsupportedOperationException("Access to deprecated filters not supported");
}
@Override
public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) {
return new SimpleBeanPropertyFilter() {
@Override
public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov, PropertyWriter writer)
throws Exception {
if (apply(pojo.getClass(), writer.getName())) {
writer.serializeAsField(pojo, jgen, prov);
} else if (!jgen.canOmitFields()) {
writer.serializeAsOmittedField(pojo, jgen, prov);
}
}
};
}
public boolean apply(Class<?> type, String name) {
Set<String> includeFields = includeMap.get(type);
Set<String> filterFields = filterMap.get(type);
if (includeFields != null && includeFields.contains(name)) {
return true;
}
if (filterFields != null && !filterFields.contains(name)) {
return true;
}
if (includeFields == null && filterFields == null) {
return true;
}
return false;
}
}
注:直接复制即可,过滤时,会调用findPropertyFilter和apply自定义过滤逻辑。
2.2.3 调用动态过滤
package com.test;
import java.util.HashMap;
import java.util.Map;
public class test3 {
public static void main(String[] args) {
TbFile tbFile = new TbFile();
tbFile.setId("条目1");
tbFile.setCode("A001");
tbFile.setName("条目");
tbFile.setStatus("待上架");
TbContainer tbContainer = new TbContainer();
tbContainer.setId("盒2");
tbContainer.setType("Box");
tbContainer.setTitle("盒");
tbContainer.setCode("02");
TbRoom tbRoom = new TbRoom();
tbRoom.setId("房间1");
tbRoom.setCode("01");
tbRoom.setName("房间");
tbRoom.setDescription("房间描述");
TbFloor tbFloor = new TbFloor();
tbFloor.setId("楼层1");
tbFloor.setName("楼层");
tbFloor.setDescription("楼层描述");
tbRoom.setFloor(tbFloor);
tbContainer.setRoom(tbRoom);
tbFile.setContainer(tbContainer);
Map<Class<?>, String[]> includeMap = new HashMap<>();
includeMap.put(TbFile.class, new String[]{"name", "container"});
includeMap.put(TbRoom.class, new String[]{"name", "id", "floor"});
includeMap.put(TbFloor.class, new String[]{"name"});
Map<Class<?>, String[]> excludeMap = new HashMap<>();
excludeMap.put(TbContainer.class, new String[]{"type","title"});
String ss = JsonUtil.toJSONString(tbFile, includeMap, excludeMap);
System.out.println(ss);
}
}
注:file类下有container对象,container对象下有room对象,room对象下有floor对象。
调用逻辑为file只保留name和container属性;container排除掉type和title属性;room只保留name、id和floor属性;floor只保留name属性。
2.2.4 输出结果:
{
"name" : "条目",
"container" : {
"id" : "盒2",
"code" : "02",
"room" : {
"id" : "房间1",
"name" : "房间",
"floor" : {
"name" : "楼层"
}
}
}
}
输出结果和上文的调用逻辑中想达到的效果一致。
至此,用jackson完美实现了对fastjson的快速替换,且实现了序列化时对多层嵌套对象的属性过滤。



591

被折叠的 条评论
为什么被折叠?



