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

Java通用三级嵌套JSON扁平化工具开发问题求助

解决JSON扁平化中的数组、数据类型处理与键唯一性问题

嘿,我仔细看了你的需求和现有代码,确实你遇到的几个点都是JSON扁平化工具里的核心问题——数组处理、多数据类型兼容,还有最关键的嵌套键唯一性保障。咱们一步步来解决:

首先先说说你现有代码里的几个明显问题:

  • 递归调用flattenJson(input)是错误的,应该传入当前遍历到的子节点(比如数组里的dataNode或者内层的JsonNode)
  • 没有维护键的前缀路径,这就导致嵌套的键会重复覆盖,没法区分层级
  • 对JsonNode的类型判断不够细致,直接转Map会丢失原始类型信息,也没法处理像Timestamp这类特殊数值

接下来是完整的解决方案,我会把每个关键点都说明白:

核心思路

要实现三级结构的扁平化(其实可以扩展到任意层级),核心是维护当前节点的键路径前缀,比如处理Rows[0].Keys.device-id时,前缀会从Rows变成Rows[0]再变成Rows[0].Keys,最终拼接成完整的唯一键。同时要针对JsonNode的不同类型做针对性处理。

完整实现代码

import com.fasterxml.jackson.databind.JsonNode;
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 JsonFlattener {

    public static Map<String, Object> flattenJson(JsonNode input) {
        Map<String, Object> resultMap = new HashMap<>();
        // 从空前缀开始递归处理根节点
        flattenNode(input, "", resultMap);
        return resultMap;
    }

    private static void flattenNode(JsonNode node, String currentPrefix, Map<String, Object> resultMap) {
        if (node.isValueNode()) {
            // 处理所有值类型:String、Long、Timestamp(这里Timestamp是数值,按Long处理)等
            Object value;
            if (node.isTextual()) {
                value = node.asText();
            } else if (node.isLong()) {
                value = node.asLong();
            } else if (node.isInt()) {
                value = node.asInt();
            } else if (node.isBoolean()) {
                value = node.asBoolean();
            } else if (node.isDouble()) {
                value = node.asDouble();
            } else {
                // 兜底处理其他类型
                value = node.asText();
            }
            // 将当前前缀作为键,值存入结果Map
            resultMap.put(currentPrefix, value);
            return;
        }

        if (node.isArray()) {
            ArrayNode arrayNode = (ArrayNode) node;
            // 遍历数组元素,用[index]作为路径后缀保证唯一性
            for (int i = 0; i < arrayNode.size(); i++) {
                JsonNode element = arrayNode.get(i);
                String newPrefix = currentPrefix.isEmpty() 
                    ? "[" + i + "]" 
                    : currentPrefix + "[" + i + "]";
                flattenNode(element, newPrefix, resultMap);
            }
            return;
        }

        if (node.isObject()) {
            ObjectNode objectNode = (ObjectNode) node;
            Iterator<Entry<String, JsonNode>> fields = objectNode.fields();
            while (fields.hasNext()) {
                Entry<String, JsonNode> field = fields.next();
                String fieldName = field.getKey();
                JsonNode childNode = field.getValue();
                // 拼接新的前缀:空前缀直接用字段名,非空前缀用"."连接父路径与当前字段
                String newPrefix = currentPrefix.isEmpty() 
                    ? fieldName 
                    : currentPrefix + "." + fieldName;
                flattenNode(childNode, newPrefix, resultMap);
            }
        }
    }
}

关键细节说明

  1. 键唯一性保障:

    • 处理对象节点时,用.拼接父前缀和当前字段名(比如Source → Rows[0].Keys.device-id)
    • 处理数组节点时,用[索引]作为后缀(比如Rows → Rows[0]),这样数组里的每个元素都有唯一的路径,不会出现键冲突
  2. 数据类型处理:

    • 通过JsonNode的isValueNode()判断是否是值类型,然后分别处理isTextual()、isLong()等具体类型,保留原始数据类型,避免转Map时的类型丢失
    • 你的示例里Timestamp是long型数值,这里会直接转成Long类型存储,完全匹配原始数据类型
  3. 递归逻辑修正:

    • 用私有方法flattenNode来递归,传递当前的键前缀和结果Map,避免每次递归都新建Map,也能正确累积所有键值对
    • 不再错误地递归根节点,而是针对当前子节点进行逐层处理

使用示例

针对你给出的示例JSON,调用flattenJson(inputNode)后,会得到类似这样的扁平化结果(部分示例):

Source: "source-1"
Rows[0].Keys.device-id: "BC04-EBH-N3K-01"
Rows[0].Keys.interface-name: "TenGigE0/0/0/39"
Rows[0].Timestamp: 1567621527656
Rows[0].inner.donm.id: "0062"
Tey.epath: "Cgetail"
...

这样既满足了三级结构的扁平化(其实可以支持任意层级),也解决了数组、数据类型和键唯一性的问题。

内容的提问来源于stack exchange,提问作者wandermonk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:08:27