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

如何用Jackson将含嵌套字段与Map的JSON反序列化为复杂Java对象

JSON反序列化为OrderCount对象:Jackson实现、替代方案及MapStruct指南

现有类结构

@Data
@SuperBuilder(toBuilder = true)
@NoArgsConstructor
public class OrderCount {
    private String entityId;
    private String entityName;
    private Integer totalTests;
    @Builder.Default
    private Map<String, Integer> products = new HashMap<>();
    private List<OrderCount> childEntities;

    @JsonAnyGetter
    public Map<String, Integer> getProducts() {
        return products;
    }

    @JsonAnySetter
    public void setAdditionalProductProperty(String name, Integer value) {
        products.put(name, value);
    }
}

待反序列化的JSON

{
    "entityId": "01",
    "entityName": "entityName",
    "totalTests": 6,
    "childEntities": [
        {
            "entityId": "01",
            "entityName": "entityName",
            "totalTests": 3,
            "productName1": 1,
            "productName2": 1,
            "productName_n": 1
        },
        {
            "entityId": "02",
            "entityName": "entityName_2",
            "totalTests": 3,
            "productName1": 1,
            "productName2": 1,
            "productName_n": 1,
            "childEntities": []     
        }
    ],
    "productName1": 2,
    "productName2": 2,
    "productName_n": 2
}

注:原JSON中06、01这类带前导零的数字不符合JSON规范,建议改为普通整数(如6、1);若必须保留,需配置Jackson允许解析前导零数字。

字段映射规则

  • entityId ↔ JSON的entityId
  • entityName ↔ JSON的entityName
  • totalTests ↔ JSON的totalTests
  • childEntities ↔ JSON的childEntities
  • 其余所有字段自动存入products Map

一、Jackson 直接实现反序列化

你的OrderCount类已经通过@JsonAnyGetter和@JsonAnySetter完成了核心配置,直接用Jackson的ObjectMapper即可完成反序列化,无需额外修改类结构。

实现代码

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.DeserializationFeature;

public class JacksonDeserializer {
    public static void main(String[] args) throws Exception {
        String json = "..."; // 替换为你的JSON字符串
        ObjectMapper objectMapper = new ObjectMapper();
        
        // 可选:如果JSON里有带前导零的数字,开启此配置
        objectMapper.enable(DeserializationFeature.ALLOW_NUMERIC_LEADING_ZEROS);
        
        OrderCount orderCount = objectMapper.readValue(json, OrderCount.class);
        // 验证结果
        System.out.println(orderCount.getEntityId());
        System.out.println(orderCount.getProducts().get("productName1"));
    }
}

原理说明

  • @JsonAnySetter会拦截所有未匹配到实体字段的JSON键值对,自动调用setAdditionalProductProperty方法将其存入products Map
  • 嵌套的childEntities会被Jackson递归反序列化为List<OrderCount>,内部的产品字段同样会被存入各自的products Map

二、替代工具方案

1. Gson 实现

Gson没有直接对应@JsonAnySetter的注解,需要自定义TypeAdapter处理额外字段:

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class OrderCountTypeAdapter extends TypeAdapter<OrderCount> {
    @Override
    public void write(JsonWriter out, OrderCount value) throws IOException {
        // 序列化逻辑按需实现
    }

    @Override
    public OrderCount read(JsonReader in) throws IOException {
        OrderCount orderCount = new OrderCount();
        Map<String, Integer> products = new HashMap<>();
        in.beginObject();
        while (in.hasNext()) {
            String name = in.nextName();
            switch (name) {
                case "entityId":
                    orderCount.setEntityId(in.nextString());
                    break;
                case "entityName":
                    orderCount.setEntityName(in.nextString());
                    break;
                case "totalTests":
                    orderCount.setTotalTests(in.nextInt());
                    break;
                case "childEntities":
                    orderCount.setChildEntities(new Gson().fromJson(in, new TypeToken<List<OrderCount>>(){}.getType()));
                    break;
                default:
                    products.put(name, in.nextInt());
                    break;
            }
        }
        in.endObject();
        orderCount.setProducts(products);
        return orderCount;
    }
}

使用时注册适配器:

Gson gson = new GsonBuilder().registerTypeAdapter(OrderCount.class, new OrderCountTypeAdapter()).create();
OrderCount orderCount = gson.fromJson(json, OrderCount.class);

2. Fastjson 实现

Fastjson提供@ExtraField注解,可直接映射额外字段到Map:
修改OrderCount类的products字段:

@ExtraField
private Map<String, Integer> products = new HashMap<>();

然后直接反序列化:

OrderCount orderCount = JSON.parseObject(json, OrderCount.class);

3. 手动解析(JsonNode)

如果不想依赖序列化库,可手动遍历JsonNode:

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 ManualParser {
    public static OrderCount parse(String json) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode root = mapper.readTree(json);
        OrderCount orderCount = new OrderCount();
        
        orderCount.setEntityId(root.get("entityId").asText());
        orderCount.setEntityName(root.get("entityName").asText());
        orderCount.setTotalTests(root.get("totalTests").asInt());
        
        // 处理childEntities
        JsonNode childNode = root.get("childEntities");
        if (childNode != null && childNode.isArray()) {
            List<OrderCount> children = new ArrayList<>();
            for (JsonNode node : childNode) {
                children.add(parse(node.toString())); // 递归解析子节点
            }
            orderCount.setChildEntities(children);
        }
        
        // 处理剩余字段
        Map<String, Integer> products = new HashMap<>();
        root.fields().forEachRemaining(entry -> {
            String key = entry.getKey();
            if (!List.of("entityId", "entityName", "totalTests", "childEntities").contains(key)) {
                products.put(key, entry.getValue().asInt());
            }
        });
        orderCount.setProducts(products);
        
        return orderCount;
    }
}

三、MapStruct 实现方案

MapStruct主要用于对象到对象的映射,这里需要先把JSON转成通用Map或JsonNode,再映射到OrderCount。

步骤1:定义Mapper接口

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Mapper
public interface OrderCountMapper {
    OrderCountMapper INSTANCE = Mappers.getMapper(OrderCountMapper.class);

    @Mapping(target = "products", source = ".", qualifiedByName = "extractProducts")
    OrderCount mapFromMap(Map<String, Object> map);

    List<OrderCount> mapListFromMapList(List<Map<String, Object>> list);

    @Named("extractProducts")
    default Map<String, Integer> extractProducts(Map<String, Object> map) {
        // 过滤指定字段,剩余字段转Integer存入Map
        return map.entrySet().stream()
                .filter(entry -> !List.of("entityId", "entityName", "totalTests", "childEntities").contains(entry.getKey()))
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> ((Number) entry.getValue()).intValue()
                ));
    }
}

步骤2:结合Jackson转Map后映射

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

public class MapStructDemo {
    public static void main(String[] args) throws Exception {
        String json = "..."; // 替换为你的JSON字符串
        ObjectMapper mapper = new ObjectMapper();
        // 先把JSON转成Map<String, Object>
        Map<String, Object> jsonMap = mapper.readValue(json, new TypeReference<Map<String, Object>>() {});
        
        OrderCount orderCount = OrderCountMapper.INSTANCE.mapFromMap(jsonMap);
        // 验证结果
        System.out.println(orderCount.getProducts().get("productName1"));
    }
}

说明

  • MapStruct会自动处理entityId、entityName、totalTests的映射
  • childEntities会被自动转成List<Map<String, Object>>,再通过mapListFromMapList递归映射为List<OrderCount>
  • 自定义的extractProducts方法负责过滤指定字段,将剩余字段转为Map<String, Integer>存入products

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:20:59