如何用Jackson扁平化LLMAsset类中的动态输入输出对象?
问题:扁平化动态输入输出字段
我定义了如下Java类:
@Data public class LLMAsset { public Object inputs = null; public Object outputs = null; }
其中inputs和outputs的类型完全由用户输入决定,可以是嵌套对象:
inputs: { name: "user", age: { number: 12 } }
也可以是简单的字符串类型:
inputs: "some string"
我需要将嵌套的输入输出结构扁平化以便索引,但尝试使用Jackson的@JsonUnwrapped注解没有效果,推测是因为字段类型为Object导致的。
我自己实现了一个可行的扁平化方案,但代码不够优雅,想寻求更优的实现方式。我的现有代码如下:
public class Asset { private Integer version; private String category; public Object input = null; public Object output = null; public Asset() {} public Asset(Object input, Object output) { this.input = input; this.output = output; } public Map<String, String> flattenIndexedFields() { Map<String, String> map = new HashMap<String, String>(); try { addKeys( "", JsonResolver.objectMapper.readTree( JsonResolver.objectMapper.writeValueAsString(getFieldsToIndex()) ), map ); } catch (JsonProcessingException e) { throw new RuntimeException(e); } return map; } private ObjectNode getFieldsToIndex() { ObjectNode inputsOutputObj = objectMapper.createObjectNode(); inputsOutputObj.set( "chain_inputs", objectMapper.valueToTree(this.getChain_inputs()) ); inputsOutputObj.set( "chain_outputs", objectMapper.valueToTree(this.getChain_outputs()) ); return inputsOutputObj; } private void addKeys( String currentPath, JsonNode jsonNode, Map<String, String> map ) { if (jsonNode.isObject()) { ObjectNode objectNode = (ObjectNode) jsonNode; Iterator<Map.Entry<String, JsonNode>> iter = objectNode.fields(); String pathPrefix = currentPath.isEmpty() ? "" : currentPath + "."; while (iter.hasNext()) { Map.Entry<String, JsonNode> entry = iter.next(); addKeys(pathPrefix + entry.getKey(), entry.getValue(), map); } } else if (jsonNode.isArray()) { ArrayNode arrayNode = (ArrayNode) jsonNode; for (int i = 0; i < arrayNode.size(); i++) { addKeys(currentPath + "[" + i + "]", arrayNode.get(i), map); } } else if (jsonNode.isValueNode()) { ValueNode valueNode = (ValueNode) jsonNode; map.put(currentPath, valueNode.asText()); } } }
优化方案
1. 核心优化方向
- 消除冗余的序列化/反序列化步骤,直接转换为
JsonNode - 简化方法职责,拆分逻辑模块
- 增加空值处理,避免潜在异常
- 优化对象复用,减少资源开销
2. 优化后的代码实现
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; public class Asset { private Integer version; private String category; public Object input = null; public Object output = null; // 复用ObjectMapper实例,避免重复创建 private static final ObjectMapper objectMapper = new ObjectMapper(); public Asset() {} public Asset(Object input, Object output) { this.input = input; this.output = output; } public Map<String, String> flattenIndexedFields() { Map<String, String> flattenedMap = new HashMap<>(); // 单独处理input字段 if (input != null) { JsonNode inputJson = objectMapper.valueToTree(input); flattenNode("chain_inputs", inputJson, flattenedMap); } // 单独处理output字段 if (output != null) { JsonNode outputJson = objectMapper.valueToTree(output); flattenNode("chain_outputs", outputJson, flattenedMap); } return flattenedMap; } /** * 递归扁平化JsonNode,将路径和值存入Map */ private void flattenNode(String currentPath, JsonNode node, Map<String, String> resultMap) { if (node.isObject()) { ObjectNode objNode = (ObjectNode) node; Iterator<Entry<String, JsonNode>> fieldIterator = objNode.fields(); String pathPrefix = currentPath.isEmpty() ? "" : currentPath + "."; while (fieldIterator.hasNext()) { Entry<String, JsonNode> field = fieldIterator.next(); flattenNode(pathPrefix + field.getKey(), field.getValue(), resultMap); } } else if (node.isArray()) { ArrayNode arrNode = (ArrayNode) node; for (int i = 0; i < arrNode.size(); i++) { flattenNode(currentPath + "[" + i + "]", arrNode.get(i), resultMap); } } else if (node.isValueNode()) { resultMap.put(currentPath, node.asText()); } } }
3. 优化细节说明
- 移除冗余转换:原代码先将对象序列化为字符串再解析为
JsonNode,优化后直接用objectMapper.valueToTree()转换,减少IO开销和对象创建 - 拆分逻辑:去掉了
getFieldsToIndex()方法,直接分别处理input和output,逻辑更清晰易懂 - 空值防护:增加了input/output的null判断,避免空指针异常
- 实例复用:将
ObjectMapper设为静态常量,避免每次调用方法都创建新实例 - 方法命名优化:把
addKeys改为flattenNode,更贴合方法职责
4. 扩展支持
如果需要保留值的原始类型(而非统一转为字符串),可以将返回的Map<String, String>改为Map<String, Object>,并在处理ValueNode时根据节点类型返回对应值:
// 替换原ValueNode处理分支 else if (node.isValueNode()) { if (node.isInt()) { resultMap.put(currentPath, node.asInt()); } else if (node.isBoolean()) { resultMap.put(currentPath, node.asBoolean()); } else if (node.isDouble()) { resultMap.put(currentPath, node.asDouble()); } else { resultMap.put(currentPath, node.asText()); } }
内容的提问来源于stack exchange,提问作者Neta yam
相关产品推荐
相关产品推荐

