Android Retrofit发送JSONArray遇Bad Request问题排查
The core issue here is how you're passing the JSON array to Retrofit. When you use JSONStringer as the @Body parameter, Retrofit's serializer (like GsonConverterFactory) doesn't treat it as a pre-built JSON string—it tries to serialize the JSONStringer object itself into JSON. That's why your OkHttp log shows the wrapped {"out":"[...]", "stack":[]} payload instead of the raw JSON array the server expects.
Here are three solid solutions to fix this:
Solution 1: Use JSONArray Directly (Simplest Fix)
Instead of JSONStringer, build a JSONArray and pass that as your @Body parameter. Retrofit's serializer will correctly convert this to the raw JSON array the server needs.
Step 1: Rewrite your payload builder
private JSONArray buildRequestArray(String PAR_IND_COD_AREA_PROC, String PAR_COD_AREA_PROC, String PAR_IND_EQPMT_DESTINO, String PAR_EQPMT_DESTINO, String PAR_IND_COD_EQPMT_PRODC, String PAR_COD_EQPMT_PRODC, String PAR_IDENT_UNMET, String PAR_COD_TIPO_ACO, String PAR_COD_GRUPO_ACO, String PAR_DTH_INI_PRAZO, String PAR_DTH_FIM_PRAZO, String PAR_COD_INTER_PEDCL, String PAR_COD_ITEM_PEDCL, String PAR_COD_CLIENTE, String PAR_NUM_CAMPA_RB, String PAR_UM_PROXI) { JSONArray requestArray = new JSONArray(); try { // Build the parameter object JSONObject params = new JSONObject(); params.put("PAR_IND_COD_AREA_PROC", PAR_IND_COD_AREA_PROC); params.put("PAR_COD_AREA_PROC", PAR_COD_AREA_PROC); params.put("PAR_IND_EQPMT_DESTINO", PAR_IND_EQPMT_DESTINO); params.put("PAR_EQPMT_DESTINO", PAR_EQPMT_DESTINO); params.put("PAR_IND_COD_EQPMT_PRODC", PAR_IND_COD_EQPMT_PRODC); params.put("PAR_COD_EQPMT_PRODC", PAR_COD_EQPMT_PRODC); params.put("PAR_IDENT_UNMET", PAR_IDENT_UNMET); params.put("PAR_COD_TIPO_ACO", PAR_COD_TIPO_ACO); params.put("PAR_COD_GRUPO_ACO", PAR_COD_GRUPO_ACO); params.put("PAR_DTH_INI_PRAZO", PAR_DTH_INI_PRAZO); params.put("PAR_DTH_FIM_PRAZO", PAR_DTH_FIM_PRAZO); params.put("PAR_COD_INTER_PEDCL", PAR_COD_INTER_PEDCL); params.put("PAR_COD_ITEM_PEDCL", PAR_COD_ITEM_PEDCL); params.put("PAR_COD_CLIENTE", PAR_COD_CLIENTE); params.put("PAR_NUM_CAMPA_RB", PAR_NUM_CAMPA_RB); params.put("PAR_UM_PROXI", PAR_UM_PROXI); // Add the object and boolean to the array requestArray.put(params); requestArray.put(true); } catch (JSONException e) { e.printStackTrace(); return null; } return requestArray; }
Step 2: Update your Retrofit interface method
@POST(Constants.MOVIMENTAR_BUSCA) Call<RetornoCompletoBuscaUmsMovimentar> getBuscaMovimentarUms(@Body JSONArray data);
Solution 2: Pass a Raw JSON String with RequestBody
If you prefer to stick with building a string, wrap it in a RequestBody with the correct media type to tell Retrofit it's raw JSON.
Step 1: Build the JSON string
private String buildRequestString(String PAR_IND_COD_AREA_PROC, String PAR_COD_AREA_PROC, String PAR_IND_EQPMT_DESTINO, String PAR_EQPMT_DESTINO, String PAR_IND_COD_EQPMT_PRODC, String PAR_COD_EQPMT_PRODC, String PAR_IDENT_UNMET, String PAR_COD_TIPO_ACO, String PAR_COD_GRUPO_ACO, String PAR_DTH_INI_PRAZO, String PAR_DTH_FIM_PRAZO, String PAR_COD_INTER_PEDCL, String PAR_COD_ITEM_PEDCL, String PAR_COD_CLIENTE, String PAR_NUM_CAMPA_RB, String PAR_UM_PROXI) { try { return new JSONStringer().array() .object() .key("PAR_IND_COD_AREA_PROC").value(PAR_IND_COD_AREA_PROC) .key("PAR_COD_AREA_PROC").value(PAR_COD_AREA_PROC) .key("PAR_IND_EQPMT_DESTINO").value(PAR_IND_EQPMT_DESTINO) .key("PAR_EQPMT_DESTINO").value(PAR_EQPMT_DESTINO) .key("PAR_IND_COD_EQPMT_PRODC").value(PAR_IND_EQPMT_PRODC) .key("PAR_COD_EQPMT_PRODC").value(PAR_COD_EQPMT_PRODC) .key("PAR_IDENT_UNMET").value(PAR_IDENT_UNMET) .key("PAR_COD_TIPO_ACO").value(PAR_COD_TIPO_ACO) .key("PAR_COD_GRUPO_ACO").value(PAR_COD_GRUPO_ACO) .key("PAR_DTH_INI_PRAZO").value(PAR_DTH_INI_PRAZO) .key("PAR_DTH_FIM_PRAZO").value(PAR_DTH_FIM_PRAZO) .key("PAR_COD_INTER_PEDCL").value(PAR_COD_INTER_PEDCL) .key("PAR_COD_ITEM_PEDCL").value(PAR_COD_ITEM_PEDCL) .key("PAR_COD_CLIENTE").value(PAR_COD_CLIENTE) .key("PAR_NUM_CAMPA_RB").value(PAR_NUM_CAMPA_RB) .key("PAR_UM_PROXI").value(PAR_UM_PROXI) .endObject() .value(true) .endArray() .toString(); } catch (JSONException e) { e.printStackTrace(); return null; } }
Step 2: Update the Retrofit interface
@POST(Constants.MOVIMENTAR_BUSCA) Call<RetornoCompletoBuscaUmsMovimentar> getBuscaMovimentarUms(@Body RequestBody data);
Step 3: Create and pass the RequestBody when calling the API
String jsonPayload = buildRequestString(/* your parameters here */); RequestBody requestBody = RequestBody.create( jsonPayload, MediaType.parse("application/json; charset=utf-8") ); Call<RetornoCompletoBuscaUmsMovimentar> call = apiService.getBuscaMovimentarUms(requestBody);
Solution 3: Use Type-Safe Java Classes (Most Maintainable)
For better type safety and maintainability, create a Java class that maps to your parameter object, then pass an Object[] containing the object and boolean as the body.
Step 1: Create the parameter class
public class MovimentarSearchParams { @SerializedName("PAR_IND_COD_AREA_PROC") private String parIndCodAreaProc; @SerializedName("PAR_COD_AREA_PROC") private String parCodAreaProc; @SerializedName("PAR_IND_EQPMT_DESTINO") private String parIndEqpmtDestino; @SerializedName("PAR_EQPMT_DESTINO") private String parEqpmtDestino; @SerializedName("PAR_IND_COD_EQPMT_PRODC") private String parIndCodEqpmtProdC; @SerializedName("PAR_COD_EQPMT_PRODC") private String parCodEqpmtProdC; @SerializedName("PAR_IDENT_UNMET") private String parIdentUnmet; @SerializedName("PAR_COD_TIPO_ACO") private String parCodTipoAco; @SerializedName("PAR_COD_GRUPO_ACO") private String parCodGrupoAco; @SerializedName("PAR_DTH_INI_PRAZO") private String parDthIniPrazo; @SerializedName("PAR_DTH_FIM_PRAZO") private String parDthFimPrazo; @SerializedName("PAR_COD_INTER_PEDCL") private String parCodInterPedcl; @SerializedName("PAR_COD_ITEM_PEDCL") private String parCodItemPedcl; @SerializedName("PAR_COD_CLIENTE") private String parCodCliente; @SerializedName("PAR_NUM_CAMPA_RB") private String parNumCampaRb; @SerializedName("PAR_UM_PROXI") private String parUmProxi; // Constructor with all parameters public MovimentarSearchParams(String parIndCodAreaProc, String parCodAreaProc, String parIndEqpmtDestino, String parEqpmtDestino, String parIndCodEqpmtProdC, String parCodEqpmtProdC, String parIdentUnmet, String parCodTipoAco, String parCodGrupoAco, String parDthIniPrazo, String parDthFimPrazo, String parCodInterPedcl, String parCodItemPedcl, String parCodCliente, String parNumCampaRb, String parUmProxi) { this.parIndCodAreaProc = parIndCodAreaProc; this.parCodAreaProc = parCodAreaProc; this.parIndEqpmtDestino = parIndEqpmtDestino; this.parEqpmtDestino = parEqpmtDestino; this.parIndCodEqpmtProdC = parIndCodEqpmtProdC; this.parCodEqpmtProdC = parCodEqpmtProdC; this.parIdentUnmet = parIdentUnmet; this.parCodTipoAco = parCodTipoAco; this.parCodGrupoAco = parCodGrupoAco; this.parDthIniPrazo = parDthIniPrazo; this.parDthFimPrazo = parDthFimPrazo; this.parCodInterPedcl = parCodInterPedcl; this.parCodItemPedcl = parCodItemPedcl; this.parCodCliente = parCodCliente; this.parNumCampaRb = parNumCampaRb; this.parUmProxi = parUmProxi; } // Optional: Getters and setters if needed }
Step 2: Update the Retrofit interface
@POST(Constants.MOVIMENTAR_BUSCA) Call<RetornoCompletoBuscaUmsMovimentar> getBuscaMovimentarUms(@Body Object[] data);
Step 3: Build and pass the request array
MovimentarSearchParams params = new MovimentarSearchParams( "1", "H", "0", "", "1", "DBQ", "", "", "", "", "", "", "", "", "", "" ); Object[] requestData = new Object[]{params, true}; Call<RetornoCompletoBuscaUmsMovimentar> call = apiService.getBuscaMovimentarUms(requestData);
Why Your Original Approach Failed
When you passed a JSONStringer to @Body, Retrofit's serializer didn't know you wanted to use its generated JSON string—it serialized the JSONStringer object itself. The out and stack fields you see in the log are internal properties of the JSONStringer class, which is why the server rejected the payload as invalid.
内容的提问来源于stack exchange,提问作者Carlos Mion

