Java Rest API无法将请求体解析为Java实体问题排查
问题:POST请求体解析异常,List大小可识别但元素属性为null
现象
- 日志显示
ProvisioningData的records列表大小为1,但每个ProvisioningRecord的name和value均为null,打印结果为null = null - 服务端日志:
size in Service: 1, null = null
- 控制器日志:
size: 1, null = null
请求体JSON(Swagger自动生成)
{ "records": [ { "name": "string", "value": "string" } ] }
相关代码
Service层代码
@POST @Path("/service/run/sequence/macless") @ApiOperation(value = "Run the install, validate or uninstall sequence for a service and get all the return parameters of that sequence") @ApiResponses(value = {@ApiResponse(code = 200, message = "OK")}) public ServiceProvisioningData runServiceSequenceMacless( @ApiParam(value = "The identifier, internally known as the lineID, that is configured on the device", required = true) @QueryParam("lineID") String lineID, @ApiParam(value = "The service name", required = true) @QueryParam("serviceName") String serviceName, @ApiParam(value = "The sequence you want to run", required = true) @QueryParam("sequenceName") Sequence sequenceName, @ApiParam(value = "The input parameters you want to overwrite for this sequence", required = false) ProvisioningData inputs ) throws ServiceException { LOG.error("size in Service: " + inputs.getRecords().size() + ", " + inputs.toString()); return restApiController.runServiceSequenceMacless(lineID, serviceName, sequenceName.toString(), inputs); }
Controller层代码
public ServiceProvisioningData runServiceSequenceMacless(String lineID, String serviceName, String sequenceName, ProvisioningData inputs) throws ServiceException { // FIRST INSERT THE INPUT PARAMETERS FOR THE DEVICE LOG.error("size: " + inputs.getRecords().size() + ", " + inputs.toString()); inputs.getRecords().forEach(record -> { try { createInputParamValueForServiceForDeviceMacless(lineID, serviceName, record.getName(), record.getValue()); } catch (ServiceException e) { LOG.error("Exception in adding input parameters macless: " + e.getMessage(), e); } }); ... }
ProvisioningData实体类
@XmlRootElement(name = "provisioningdata") @XmlType(propOrder = { "records" }) @JsonInclude(JsonInclude.Include.NON_NULL) public class ProvisioningData { public static void registerToContextProvider(){ try { CustomContextProvider.registerClass(ProvisioningData.class); CustomContextProvider.registerArray("records"); } catch (Exception e){ Log.error("Could not register ProvisioningData: "+e.getMessage()); } } /** The properties. */ private List<ProvisioningRecord> records = new ArrayList<>(); /** * Instantiates a new ProvisioningData. */ public ProvisioningData(List<ProvisioningRecord> records) { this.records = records; } /** * Instantiates a new ProvisioningData. */ public ProvisioningData() { } /** * Gets the records. * * @return the records */ @XmlElement(name = "records") public List<ProvisioningRecord> getRecords() { return records; } public void setRecords(List<ProvisioningRecord> res) { this.records = res; } public void addEntry(ScriptVariableValueRecord rec) { records.add(new ProvisioningRecord(rec)); } @Override public String toString() { StringBuilder recs = new StringBuilder(); for (ProvisioningRecord record : records){ recs.append(record.getName()).append(" = ").append(record.getValue()).append(", "); } return recs.toString(); } }
ProvisioningRecord实体类
@XmlRootElement(name = "record") @XmlType(propOrder = { "name", "value" }) @JsonInclude(JsonInclude.Include.NON_NULL) public class ProvisioningRecord { public static void registerToContextProvider(){ try { CustomContextProvider.registerClass(ProvisioningRecord.class); } catch (Exception e){ Log.error("Could not register ProvisioningRecord: "+e.getMessage()); } } /** The name. */ private String name; /** The value. */ private String value; public ProvisioningRecord() { } public ProvisioningRecord(ScriptVariableValueRecord record) { name = record.getName(); value = record.getValue(); } public ProvisioningRecord(ScriptVariableValueModel model) { name = model.getName(); value = model.getValue(); } /** * Gets the variable name. * * @return the variable name */ @XmlElement(name = "name") public String getName() { return name; } /** * Gets the variable value. * * @return the variable value */ @XmlElement(name = "value") public String getValue() { return value; } }
解决方法
问题根源
ProvisioningRecord类仅包含getter方法,缺少setter方法。JSON序列化框架(如Jackson)无法将请求体中的name和value字段值注入到对象实例中,导致属性为null;而列表大小能被识别,是因为框架可以创建空的ProvisioningRecord实例并添加到列表中。
修复步骤
- 为
ProvisioningRecord的name和value属性添加setter方法:
public void setName(String name) { this.name = name; } public void setValue(String value) { this.value = value; }
- (可选)若使用JAX-RS,可添加
@JsonProperty注解明确字段映射关系,确保与请求体的key完全对应:
@XmlElement(name = "name") @JsonProperty("name") public String getName() { return name; } @JsonProperty("name") public void setName(String name) { this.name = name; } @XmlElement(name = "value") @JsonProperty("value") public String getValue() { return value; } @JsonProperty("value") public void setValue(String value) { this.value = value; }
- 验证请求头是否包含
Content-Type: application/json,确保服务端按JSON格式解析请求体。
内容的提问来源于stack exchange,提问作者Amber Akkermans
相关产品推荐
相关产品推荐

