使用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
相关产品推荐
相关产品推荐

