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

使用Jackson序列化含动态属性名嵌套对象的JSON问题

解决Spring Boot中动态属性名的JSON序列化问题

针对你需要生成的包含动态属性名的JSON结构,我们可以通过两种方式实现正确的序列化:利用Jackson注解封装类,或者直接使用Map类型字段。以下是具体实现:

方案一:封装类+Jackson注解(推荐,面向对象设计)

这种方式通过自定义类配合Jackson的@JsonAnyGetter和@JsonAnySetter注解,将动态键值对序列化为JSON对象的属性,同时保留面向对象的封装性。

1. 实现TrackResponse类

对应JSON中track节点的动态属性(键为跟踪号,值为状态):

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import java.util.HashMap;
import java.util.Map;

public class TrackResponse {
    private final Map<String, String> trackStatusMap = new HashMap<>();

    // 将Map中的键值对序列化为JSON对象的动态属性
    @JsonAnyGetter
    public Map<String, String> getTrackStatusMap() {
        return trackStatusMap;
    }

    // 反序列化时接收动态属性(如果需要从JSON解析到对象)
    @JsonAnySetter
    public void setTrackStatus(String trackingNumber, String status) {
        trackStatusMap.put(trackingNumber, status);
    }

    // 便捷方法:通过跟踪号设置状态(适配业务逻辑)
    public void setTrackStatusByNumber(String trackingNumber, String status) {
        trackStatusMap.put(trackingNumber, status);
    }
}

2. 实现ShipmentsResponse类

对应JSON中shipments节点的动态属性(键为运单号,值为包装类型列表):

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ShipmentsResponse {
    private final Map<String, List<String>> shipmentPackagesMap = new HashMap<>();

    @JsonAnyGetter
    public Map<String, List<String>> getShipmentPackagesMap() {
        return shipmentPackagesMap;
    }

    @JsonAnySetter
    public void setShipmentPackages(String shipmentId, List<String> packages) {
        shipmentPackagesMap.put(shipmentId, packages);
    }

    // 便捷方法:通过运单号设置包装列表
    public void setShipmentPackagesById(String shipmentId, List<String> packages) {
        shipmentPackagesMap.put(shipmentId, packages);
    }
}

3. 实现PricingResponse类

对应JSON中pricing节点的动态属性(键为国家代码,值为价格):

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import java.util.HashMap;
import java.util.Map;

public class PricingResponse {
    private final Map<String, Double> pricingMap = new HashMap<>();

    @JsonAnyGetter
    public Map<String, Double> getPricingMap() {
        return pricingMap;
    }

    @JsonAnySetter
    public void setPrice(String countryCode, Double price) {
        pricingMap.put(countryCode, price);
    }
}

4. 修正AggregatedResponse类

注意JSON中的track字段对应类中的tracking属性,需要添加@JsonProperty注解映射字段名:

import com.fasterxml.jackson.annotation.JsonProperty;

public class AggregatedResponse {
    @JsonProperty("track")
    private TrackResponse tracking;
    private ShipmentsResponse shipments;
    private PricingResponse pricing;

    public ShipmentsResponse getShipments() {
        return shipments;
    }

    public void setShipmentsResponse(ShipmentsResponse value) {
        this.shipments = value;
    }

    public TrackResponse getTrackResponse() {
        return tracking;
    }

    public void setTrackResponse(TrackResponse trackResponse) {
        this.tracking = trackResponse;
    }

    public PricingResponse getPricing() {
        return pricing;
    }

    public void setPricing(PricingResponse value) {
        this.pricing = value;
    }
}

5. 使用示例(组装响应数据)

// 模拟从外部API获取的数据
String trackingNumber = "123456789";
String trackStatus = "COLLECTING";

String shipmentId = "987654321";
List<String> packages = List.of("BOX", "BOX", "PALLET");

// 组装响应
AggregatedResponse aggregatedResponse = new AggregatedResponse();

// 设置跟踪数据
TrackResponse trackResponse = new TrackResponse();
trackResponse.setTrackStatusByNumber(trackingNumber, trackStatus);
aggregatedResponse.setTrackResponse(trackResponse);

// 设置运单数据
ShipmentsResponse shipmentsResponse = new ShipmentsResponse();
shipmentsResponse.setShipmentPackagesById(shipmentId, packages);
aggregatedResponse.setShipmentsResponse(shipmentsResponse);

// 设置价格数据
PricingResponse pricingResponse = new PricingResponse();
pricingResponse.setPrice("NL", 14.242090605778);
pricingResponse.setPrice("CN", 20.503467806384);
aggregatedResponse.setPricing(pricingResponse);

方案二:直接使用Map字段(简洁高效)

如果不需要额外的封装逻辑,可以直接在AggregatedResponse中使用Map类型字段,Jackson会自动将Map序列化为JSON对象:

import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import java.util.Map;

public class AggregatedResponse {
    @JsonProperty("track")
    private Map<String, String> tracking;
    private Map<String, List<String>> shipments;
    private Map<String, Double> pricing;

    // getter和setter方法
    public Map<String, String> getTracking() {
        return tracking;
    }

    public void setTracking(Map<String, String> tracking) {
        this.tracking = tracking;
    }

    public Map<String, List<String>> getShipments() {
        return shipments;
    }

    public void setShipments(Map<String, List<String>> shipments) {
        this.shipments = shipments;
    }

    public Map<String, Double> getPricing() {
        return pricing;
    }

    public void setPricing(Map<String, Double> pricing) {
        this.pricing = pricing;
    }
}

使用示例

// 模拟外部API数据
Map<String, String> trackMap = Map.of("123456789", "COLLECTING");
Map<String, List<String>> shipmentsMap = Map.of("987654321", List.of("BOX", "BOX", "PALLET"));
Map<String, Double> pricingMap = Map.of("NL", 14.242090605778, "CN", 20.503467806384);

// 组装响应
AggregatedResponse aggregatedResponse = new AggregatedResponse();
aggregatedResponse.setTracking(trackMap);
aggregatedResponse.setShipments(shipmentsMap);
aggregatedResponse.setPricing(pricingMap);

两种方案都能生成你需要的JSON结构,方案一适合需要添加业务逻辑校验或扩展的场景,方案二更适合快速实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 22:15:28