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

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实例并添加到列表中。

修复步骤

  1. 为ProvisioningRecord的name和value属性添加setter方法:
public void setName(String name) {
    this.name = name;
}

public void setValue(String value) {
    this.value = value;
}
  1. (可选)若使用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;
}
  1. 验证请求头是否包含Content-Type: application/json,确保服务端按JSON格式解析请求体。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 20:48:19