Android Retrofit响应解析错误求助:两种响应格式适配问题
Hey there! Let's tackle this Retrofit response parsing issue you're facing. The core problem here is that your API returns two different formats for the message field—an object on success, a plain string on error—which breaks fixed POJO parsing. Here are two practical, clean approaches to handle this:
JsonElement for Flexible Message Parsing This is the simplest quick fix. We'll define the message field as a JsonElement (from Gson) to accept both object and string types, then manually parse it based on the response's code or status.
Step 1: Create a Unified Response POJO
import com.google.gson.JsonElement; public class ApiResponse { private Results results; public Results getResults() { return results; } public static class Results { private JsonElement message; // Accepts any JSON type private String status; private int code; // Getters for all fields public JsonElement getMessage() { return message; } public String getStatus() { return status; } public int getCode() { return code; } } } // Separate POJO for the success message object public class SuccessMessage { private long en; private String ete; private String vto; private boolean er; // Getters for all fields }
Step 2: Parse the Response Conditionally
When handling the Retrofit callback, check the response status/code to decide how to parse the message:
call.enqueue(new Callback<ApiResponse>() { @Override public void onResponse(Call<ApiResponse> call, Response<ApiResponse> response) { if (response.isSuccessful()) { ApiResponse.Results results = response.body().getResults(); if (results.getCode() == 200 && "Success".equals(results.getStatus())) { // Parse message as the success object SuccessMessage successMsg = new Gson().fromJson(results.getMessage(), SuccessMessage.class); // Do something with successMsg (e.g., access successMsg.getEn()) } else if (results.getCode() == 204 && "No Content".equals(results.getStatus())) { // Parse message as a string String errorMsg = results.getMessage().getAsString(); // Handle the error case } } } @Override public void onFailure(Call<ApiResponse> call, Throwable t) { // Handle network failures } });
For a more elegant, automated solution, create a custom TypeAdapter that detects the message type and parses it into the appropriate class. This avoids manual parsing in every callback.
Step 1: Define a Sealed Message Class & TypeAdapter
import com.google.gson.*; import java.io.IOException; public class MessageTypeAdapter extends TypeAdapter<Message> { private final Gson gson = new Gson(); @Override public void write(JsonWriter out, Message value) throws IOException { // Only needed if you're sending requests with this type; skip if not gson.toJson(value, Message.class, out); } @Override public Message read(JsonReader in) throws IOException { JsonToken token = in.peek(); if (token == JsonToken.STRING) { // Error case: message is a string return new ErrorMessage(in.nextString()); } else if (token == JsonToken.BEGIN_OBJECT) { // Success case: message is an object return gson.fromJson(in, SuccessMessage.class); } throw new JsonParseException("Unexpected token type for 'message' field: " + token); } // Abstract base class for messages public abstract static class Message {} // Success message implementation public static class SuccessMessage extends Message { private long en; private String ete; private String vto; private boolean er; // Getters for all fields } // Error message implementation public static class ErrorMessage extends Message { private final String message; public ErrorMessage(String message) { this.message = message; } public String getMessage() { return message; } } }
Step 2: Register the TypeAdapter with Retrofit
Add the custom Gson instance to your Retrofit setup:
Gson customGson = new GsonBuilder() .registerTypeAdapter(MessageTypeAdapter.Message.class, new MessageTypeAdapter()) .create(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(YOUR_BASE_URL) .addConverterFactory(GsonConverterFactory.create(customGson)) .build();
Step 3: Update the Response POJO
Now you can use the Message type directly in your response class:
public class ApiResponse { private Results results; public Results getResults() { return results; } public static class Results { private MessageTypeAdapter.Message message; private String status; private int code; // Getters for all fields } }
Step 4: Handle the Response Cleanly
No more manual parsing—just check the message type:
call.enqueue(new Callback<ApiResponse>() { @Override public void onResponse(Call<ApiResponse> call, Response<ApiResponse> response) { if (response.isSuccessful()) { ApiResponse.Results results = response.body().getResults(); if (results.getMessage() instanceof MessageTypeAdapter.SuccessMessage) { MessageTypeAdapter.SuccessMessage successMsg = (MessageTypeAdapter.SuccessMessage) results.getMessage(); // Handle success } else if (results.getMessage() instanceof MessageTypeAdapter.ErrorMessage) { MessageTypeAdapter.ErrorMessage errorMsg = (MessageTypeAdapter.ErrorMessage) results.getMessage(); // Handle error } } } @Override public void onFailure(Call<ApiResponse> call, Throwable t) { // Handle network failures } });
A quick note: If you have control over the backend, it's always better to standardize the response format (e.g., make message always an object with a text field for errors) to avoid these parsing hoops. But if that's not an option, either of these methods will work perfectly.
内容的提问来源于stack exchange,提问作者mavilla vishnu vardhan

