Jackson复杂嵌套结构序列化反序列化代码优化方案问询
问题描述
我正在为游戏社区开发基于Spring的可扩展、模块化小型自定义CMS后端,需要将模块配置、内容等信息以JSON字符串形式存储到关系型数据库(无法使用NoSQL,指定用Java开发)。当前要存储<String, Object>类型的嵌套Map,序列化相对容易,但反序列化时Jackson默认返回LinkedHashMap,我自己写了一套实现,但方案比较粗糙,现在寻求代码性能优化或更简洁的实现方式。
现有实现代码
ObjectWrapper类
@Data @NoArgsConstructor @AllArgsConstructor public class ObjectWrapper { private String clazz; private Object value; }
CustomHashMapJsonConverter类
@Log4j2 public class CustomHashMapJsonConverter implements AttributeConverter<HashMap<String, Object>, String> { private static ObjectMapper objectMapper = getDefaultObjectMapper(); private static ObjectMapper getDefaultObjectMapper() { ObjectMapper defaultObjectMapper = new ObjectMapper(); defaultObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return defaultObjectMapper; } @Override public String convertToDatabaseColumn(HashMap<String, Object> attributeMap) { String jsonResult = null; ObjectWrapper topLevelWrapper = wrapMapAndContents(attributeMap); ObjectWriter objectWriter = objectMapper.writer(); objectWriter = objectWriter.with(SerializationFeature.INDENT_OUTPUT); try { jsonResult = objectWriter.writeValueAsString(objectMapper.valueToTree(topLevelWrapper)); } catch (JsonProcessingException | IllegalArgumentException e) { log.error("Error while serializing a map: ", e); } return jsonResult; } @Override public HashMap<String, Object> convertToEntityAttribute(String dbJsonData) { HashMap<String, Object> resultMap = new HashMap<>(); SimpleModule module = new SimpleModule(); module.addDeserializer(ObjectWrapper.class, new CustomHashMapJsonDeserializer()); objectMapper.registerModule(module); try { ObjectWrapper topLevelWrapper = objectMapper.readValue(dbJsonData, ObjectWrapper.class); resultMap = (HashMap<String, Object>) unwrapObjectWrapperContent(topLevelWrapper); } catch (JsonProcessingException | ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { log.error("Error while deserializing a retrieved JSON from the DB: ", e); } return resultMap; } public ObjectWrapper wrapMapAndContents(Map<String, Object> map) { HashMap<String, Object> resultMap = new HashMap<>(); for (Map.Entry<String, Object> entry : map.entrySet()) { if (entry.getValue() instanceof Map) { resultMap.put(entry.getKey(), wrapMapAndContents((Map<String, Object>) entry.getValue())); } else if (entry.getValue() instanceof List) { resultMap.put(entry.getKey(), wrapListAndContents((List<Object>) entry.getValue())); } else { resultMap.put(entry.getKey(), wrapObject(entry.getValue())); } } return wrapObject(resultMap); } public ObjectWrapper wrapListAndContents(List<Object> list) { ArrayList<Object> resultList = new ArrayList<>(); for (Object listObject : list) { if (listObject instanceof Map) { resultList.add(wrapMapAndContents((Map<String, Object>) listObject)); } else if (listObject instanceof List) { resultList.add(wrapListAndContents((List<Object>) listObject)); } else { resultList.add(wrapObject(listObject)); } } return wrapObject(resultList); } public ObjectWrapper wrapObject(Object object) { return new ObjectWrapper(object.getClass().toString().split(" ")[1], object); } public Object unwrapObjectWrapperContent(ObjectWrapper topLevelWrapper) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { Object topLevelObject = Class.forName(topLevelWrapper.getClazz()).cast(topLevelWrapper.getValue()); if (topLevelObject instanceof Map) { Map<String, ObjectWrapper> topLevelMap = (Map<String, ObjectWrapper>) topLevelObject; Map<String, Object> newMap = (Map<String, Object>) Class.forName(topLevelWrapper.getClazz()) .getDeclaredConstructor().newInstance(); for (Map.Entry<String, ObjectWrapper> entry : topLevelMap.entrySet()) { newMap.put(entry.getKey(), unwrapObjectWrapperContent(entry.getValue())); } topLevelObject = newMap; } else if (topLevelObject instanceof List) { List<ObjectWrapper> listOfObjectWrappers = (List<ObjectWrapper>) topLevelObject; List<Object> newList = (List<Object>) Class.forName(topLevelWrapper.getClazz()) .getDeclaredConstructor().newInstance(); for (ObjectWrapper objectWrapper : listOfObjectWrappers) { newList.add(unwrapObjectWrapperContent(objectWrapper)); } topLevelObject = newList; } return topLevelObject; } }
CustomHashMapJsonDeserializer类
@Log4j2 public class CustomHashMapJsonDeserializer extends JsonDeserializer<ObjectWrapper> { private final ObjectMapper objectMapper = getDefaultObjectMapper(); private static ObjectMapper getDefaultObjectMapper() { ObjectMapper defaultObjectMapper = new ObjectMapper(); defaultObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return defaultObjectMapper; } @Override public ObjectWrapper deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { JsonNode node = p.getCodec().readTree(p); return convertNode(node); } private ObjectWrapper convertNode(JsonNode node) throws IOException, IllegalArgumentException { ObjectWrapper topLevelWrapper = objectMapper.readValue(objectMapper.treeAsTokens(node), ObjectWrapper.class); try { // TODO: Ugly workaround with mapper.treeToValue(mapper.valueToTree(object)) to // get back the JsonNode in order to properly deserialize the objects into their // respective types topLevelWrapper.setValue(objectMapper.treeToValue(objectMapper.valueToTree(topLevelWrapper.getValue()), Class.forName(topLevelWrapper.getClazz()))); if (topLevelWrapper.getValue() instanceof Map) { Map<String, Object> mapObject = (Map<String, Object>) topLevelWrapper.getValue(); for (Map.Entry<String, Object> entry : mapObject.entrySet()) { // TODO: Ugly workaround with mapper.valueToTree(object) to get back the // JsonNode in order to properly deserialize the objects into their respective // types mapObject.put(entry.getKey(), convertNode(objectMapper.valueToTree(entry.getValue()))); } topLevelWrapper.setValue(mapObject); } else if (topLevelWrapper.getValue() instanceof List) { List<Object> listObject = (List<Object>) topLevelWrapper.getValue(); List<Object> listObjectCopy = (List<Object>) Class.forName(topLevelWrapper.getClazz()) .getDeclaredConstructor().newInstance(); for (Object objectInList : listObject) { // TODO: Ugly workaround with mapper.valueToTree(object) to get back the // JsonNode in order to properly deserialize the objects into their respective // types listObjectCopy.add(convertNode(objectMapper.valueToTree(objectInList))); } topLevelWrapper.setValue(listObjectCopy); listObject = null; } // TODO: Too many damn exceptions... Isn't there a way how I can do all this // without so many possible Exceptions being thrown? } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException e) { log.error("Error while deserializing JSON: ", e); } return topLevelWrapper; } }
优化方案与实现
核心优化点
- 简化类型存储逻辑:替换不可靠的类名解析方式,直接用
Class.getName()获取全限定类名 - 复用ObjectMapper与模块:避免每次反序列化都重复注册模块,提升性能
- 减少中间转换步骤:去掉反复转换JsonNode的冗余操作,直接通过自定义序列化/反序列化器处理嵌套结构
- 优化反射操作:缓存常用集合类的Class实例,减少反射开销
优化后的代码
ObjectWrapper类(简化字段名)
@Data @NoArgsConstructor @AllArgsConstructor public class ObjectWrapper { private String type; private Object value; }
整合式转换器(合并序列化/反序列化逻辑)
@Log4j2 public class NestedMapJsonConverter implements AttributeConverter<HashMap<String, Object>, String> { private static final ObjectMapper OBJECT_MAPPER = initObjectMapper(); // 缓存常用集合类,避免重复反射 private static final Map<String, Class<?>> CACHED_CLASSES = Map.of( HashMap.class.getName(), HashMap.class, ArrayList.class.getName(), ArrayList.class ); private static ObjectMapper initObjectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); SimpleModule module = new SimpleModule(); module.addSerializer(ObjectWrapper.class, new ObjectWrapperSerializer()); module.addDeserializer(ObjectWrapper.class, new ObjectWrapperDeserializer()); mapper.registerModule(module); return mapper; } @Override public String convertToDatabaseColumn(HashMap<String, Object> attributeMap) { try { ObjectWrapper wrapper = wrapNestedStructure(attributeMap); // 生产环境去掉格式化,减少JSON体积和序列化时间 return OBJECT_MAPPER.writeValueAsString(wrapper); } catch (JsonProcessingException e) { log.error("序列化嵌套Map失败", e); return null; } } @Override public HashMap<String, Object> convertToEntityAttribute(String dbJsonData) { if (dbJsonData == null || dbJsonData.isEmpty()) { return new HashMap<>(); } try { ObjectWrapper wrapper = OBJECT_MAPPER.readValue(dbJsonData, ObjectWrapper.class); return (HashMap<String, Object>) unwrapNestedStructure(wrapper); } catch (Exception e) { log.error("反序列化JSON失败", e); return new HashMap<>(); } } private ObjectWrapper wrapNestedStructure(Object obj) { if (obj instanceof Map) { Map<String, Object> wrappedMap = new HashMap<>(); for (Map.Entry<String, Object> entry : ((Map<String, Object>) obj).entrySet()) { wrappedMap.put(entry.getKey(), wrapNestedStructure(entry.getValue())); } return new ObjectWrapper(obj.getClass().getName(), wrappedMap); } else if (obj instanceof List) { List<Object> wrappedList = new ArrayList<>(); for (Object item : ((List<Object>) obj)) { wrappedList.add(wrapNestedStructure(item)); } return new ObjectWrapper(obj.getClass().getName(), wrappedList); } else { return new ObjectWrapper(obj.getClass().getName(), obj); } } private Object unwrapNestedStructure(ObjectWrapper wrapper) throws Exception { String type = wrapper.getType(); Class<?> clazz = CACHED_CLASSES.getOrDefault(type, Class.forName(type)); Object value = wrapper.getValue(); if (Map.class.isAssignableFrom(clazz)) { Map<String, Object> resultMap = (Map<String, Object>) clazz.getDeclaredConstructor().newInstance(); for (Map.Entry<String, Object> entry : ((Map<String, Object>) value).entrySet()) { resultMap.put(entry.getKey(), unwrapNestedStructure((ObjectWrapper) entry.getValue())); } return resultMap; } else if (List.class.isAssignableFrom(clazz)) { List<Object> resultList = (List<Object>) clazz.getDeclaredConstructor().newInstance(); for (Object item : ((List<Object>) value)) { resultList.add(unwrapNestedStructure((ObjectWrapper) item)); } return resultList; } else { return clazz.cast(value); } } // 自定义序列化器,直接写入ObjectWrapper字段 static class ObjectWrapperSerializer extends JsonSerializer<ObjectWrapper> { @Override public void serialize(ObjectWrapper value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeStartObject(); gen.writeStringField("type", value.getType()); gen.writeObjectField("value", value.getValue()); gen.writeEndObject(); } } // 自定义反序列化器,直接解析字段并转换类型 static class ObjectWrapperDeserializer extends JsonDeserializer<ObjectWrapper> { @Override public ObjectWrapper deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); String type = node.get("type").asText(); JsonNode valueNode = node.get("value"); try { Class<?> clazz = CACHED_CLASSES.getOrDefault(type, Class.forName(type)); Object value; if (Map.class.isAssignableFrom(clazz)) { value = ctxt.readValue(valueNode.traverse(), Map.class); } else if (List.class.isAssignableFrom(clazz)) { value = ctxt.readValue(valueNode.traverse(), List.class); } else { value = ctxt.readValue(valueNode.traverse(), clazz); } return new ObjectWrapper(type, value); } catch (ClassNotFoundException e) { throw new IOException("无法找到指定类型: " + type, e); } } } }
额外建议
- 如果不需要严格保留原Map/List的实现类(比如只需要兼容Map接口),可以直接用
LinkedHashMap/ArrayList代替反射创建实例,进一步提升性能 - 可以添加类型校验逻辑,避免非法类型被序列化到数据库中
- 针对高频访问的配置数据,可以添加本地缓存,减少数据库读取和反序列化次数
内容的提问来源于stack exchange,提问作者Browbeat
相关产品推荐
相关产品推荐

