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

如何将动态多层级JSON转换为可跨项目复用的Java POJO

动态多层级无固定结构JSON转可复用Java实体方案

不需要为每个JSON结构单独编写POJO、支持任意层级嵌套、可跨项目复用的核心实现思路是递归解析任意类型的JSON值,用通用存储结构代替固定字段定义,以下两种经过生产验证的方案都可以解决@JsonAnySetter、单层级JsonNode无法处理嵌套结构的问题:

方案一:自定义通用动态实体(推荐,易用性最高)

之前用@JsonAnySetter只能处理单层级结构,本质是没有对传入的嵌套Map、List类型的值做二次递归解析。我们可以基于@JsonAnySetter扩展,实现一次编写、所有项目可直接复用的通用实体类,内部自动递归处理嵌套对象、数组类型,不需要针对业务字段做任何硬编码:

import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DynamicJsonEntity {
    // 存储所有动态键值对,支持任意键名
    private final Map<String, Object> properties = new HashMap<>();
    // 全局复用序列化配置,可根据自己的业务需求扩展
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    // 所有未知键的赋值统一走这个入口
    @JsonAnySetter
    public void set(String key, Object value) {
        properties.put(key, parseValue(value));
    }

    // 递归解析值类型,支持任意深度嵌套
    private Object parseValue(Object value) {
        if (value instanceof Map) {
            // 嵌套JSON对象递归转成DynamicJsonEntity实例
            DynamicJsonEntity nestedEntity = new DynamicJsonEntity();
            ((Map<?, ?>) value).forEach((k, v) -> nestedEntity.set(k.toString(), v));
            return nestedEntity;
        } else if (value instanceof List) {
            // JSON数组递归处理每一个元素
            List<Object> parsedList = new ArrayList<>();
            for (Object item : (List<?>) value) {
                parsedList.add(parseValue(item));
            }
            return parsedList;
        }
        // 字符串、数字、布尔值等基本类型直接存储
        return value;
    }

    // 路径式取值,支持.取对象属性、[index]取数组下标
    public Object get(String path) {
        String[] nodes = path.split("\\.");
        Object current = this.properties;
        for (String node : nodes) {
            if (current == null) return null;
            if (node.contains("[")) {
                String fieldName = node.substring(0, node.indexOf("["));
                int index = Integer.parseInt(node.substring(node.indexOf("[")+1, node.indexOf("]")));
                current = ((Map<?,?>)current).get(fieldName);
                if (current == null) return null;
                current = ((List<?>)current).get(index);
            } else {
                if (current instanceof DynamicJsonEntity) {
                    current = ((DynamicJsonEntity) current).properties.get(node);
                } else if (current instanceof Map) {
                    current = ((Map<?,?>)current).get(node);
                } else {
                    return null;
                }
            }
        }
        return current;
    }

    // JSON字符串转实体的统一入口,全项目直接调用
    public static DynamicJsonEntity fromJson(String json) throws Exception {
        return OBJECT_MAPPER.readValue(json, DynamicJsonEntity.class);
    }

    // 按需转成原生Map结构使用
    public Map<String, Object> toMap() {
        return this.properties;
    }
}

针对你给出的示例JSON,使用方式如下:

public class Test {
    public static void main(String[] args) throws Exception {
        String json = "{\n" +
                "    \"name\" : \"sahal\",\n" +
                "    \"address\" : [\n" +
                "        {\"state\" : \"FL\"},\n" +
                "        {\"country\" : \"FL\",\"city\" : {\"type\" : \"municipality\",\"value\" : \"California City\"}},\n" +
                "        {\"pin\" : \"87876\"}\n" +
                "    ]\n" +
                "}";
        DynamicJsonEntity entity = DynamicJsonEntity.fromJson(json);
        // 取普通属性,后续键名改成FirstName直接修改路径参数即可,不需要改类代码
        System.out.println(entity.get("name")); // 输出 sahal
        // 取嵌套数组里的深层属性
        System.out.println(entity.get("address[1].city.value")); // 输出 California City
        System.out.println(entity.get("address[2].pin")); // 输出 87876
    }
}

这个方案的优势:

  • 无业务耦合:类可以直接抽成公共工具包的基础类,所有项目引入就能用,不管JSON的键名怎么改、嵌套多少层、结构怎么调整都不需要修改类代码
  • 易用性强:封装了路径式取值方法,不需要手动遍历多层结构做类型转换
  • 兼容性好:如果后续某个字段固定需要强类型,直接在类里添加对应字段加@JsonProperty注解即可,不影响原有动态字段的解析

方案二:通用JSON转Map工具类(零实体封装)

如果不想维护自定义实体类,可以写一个递归解析的工具类,直接把任意JSON转成嵌套的Map/List/基本类型结构,工具类同样可以跨项目复用:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JsonUtils {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    // 任意JSON转嵌套Map结构入口
    public static Map<String, Object> jsonToMap(String json) throws Exception {
        JsonNode root = OBJECT_MAPPER.readTree(json);
        return (Map<String, Object>) parseNode(root);
    }

    private static Object parseNode(JsonNode node) {
        if (node.isObject()) {
            Map<String, Object> map = new HashMap<>();
            node.fields().forEachRemaining(entry -> map.put(entry.getKey(), parseNode(entry.getValue())));
            return map;
        } else if (node.isArray()) {
            List<Object> list = new ArrayList<>();
            node.forEach(item -> list.add(parseNode(item)));
            return list;
        } else if (node.isTextual()) {
            return node.asText();
        } else if (node.isNumber()) {
            return node.numberValue();
        } else if (node.isBoolean()) {
            return node.asBoolean();
        }
        return node.isNull() ? null : node.asText();
    }
}

这个方案的缺点是取嵌套值时需要多层强转,代码可读性差,容易出现类型转换异常,适合临时处理JSON的场景,不适合在核心业务逻辑里大量使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.18 16:15:43