You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 22:05:29