能否使用单个类通过Retrofit解析不同JSON响应?如何构建该类?
Since you're using Gson for deserialization (evident from @SerializedName and @Expose annotations), there are two clean approaches to handle both JSON response types with a single JsonResponse class. Let's break them down:
Approach 1: Type-Safe Generic Response + Custom Deserializer
This method keeps your existing DataSync class intact and pairs it with a corresponding DataWeather class, using a generic JsonResponse and custom deserializer to automatically pick the correct data type during parsing.
Step 1: Create Generic JsonResponse Class
Replace your specific JsonResponse classes with a generic version that works for any data type:
public class JsonResponse<T> { @SerializedName("success") @Expose private Boolean success; @SerializedName("data") @Expose private T data; // Getters and Setters public Boolean getSuccess() { return success; } public void setSuccess(Boolean success) { this.success = success; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
Step 2: Add DataWeather Class for the First Response
Keep your existing DataSync class, and create a DataWeather class to map the first JSON structure:
// DataWeather.java public class DataWeather { @SerializedName("weather") @Expose private List<Weather> weather; @SerializedName("total") @Expose private Integer total; // Getters and Setters public List<Weather> getWeather() { return weather; } public void setWeather(List<Weather> weather) { this.weather = weather; } public Integer getTotal() { return total; } public void setTotal(Integer total) { this.total = total; } // Inner class for weather items public static class Weather { @SerializedName("location") @Expose private String location; @SerializedName("condition") @Expose private String condition; // Getters and Setters public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public String getCondition() { return condition; } public void setCondition(String condition) { this.condition = condition; } } }
Step 3: Implement Custom Deserializer
Create a deserializer that checks the fields in the data object to determine which data class to use:
public class JsonResponseDeserializer implements JsonDeserializer<JsonResponse<?>> { @Override public JsonResponse<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject rootObject = json.getAsJsonObject(); JsonResponse<?> response = new JsonResponse<>(); // Parse success flag response.setSuccess(rootObject.get("success").getAsBoolean()); // Check data fields to decide which type to parse JsonObject dataObject = rootObject.getAsJsonObject("data"); if (dataObject.has("weather")) { DataWeather weatherData = context.deserialize(dataObject, DataWeather.class); response.setData(weatherData); } else if (dataObject.has("syncRequired")) { DataSync syncData = context.deserialize(dataObject, DataSync.class); response.setData(syncData); } else { throw new JsonParseException("Unrecognized data structure in API response"); } return response; } }
Step 4: Register Deserializer and Parse
Initialize Gson with the deserializer, then parse and handle your response:
// Set up Gson with custom deserializer Gson gson = new GsonBuilder() .registerTypeAdapter(JsonResponse.class, new JsonResponseDeserializer()) .create(); // Parse JSON string JsonResponse<?> apiResponse = gson.fromJson(yourJsonString, JsonResponse.class); // Handle data based on type if (apiResponse.getData() instanceof DataWeather) { DataWeather weatherData = (DataWeather) apiResponse.getData(); // Work with weather list and total count } else if (apiResponse.getData() instanceof DataSync) { DataSync syncData = (DataSync) apiResponse.getData(); // Check sync requirement and location count }
Approach 2: Single Data Class with Optional Fields
If you prefer a simpler setup (at the cost of slightly less type safety), create a single Data class that includes all possible fields from both responses. Gson automatically ignores missing fields during parsing.
Step 1: Create Unified Data Class
public class Data { @SerializedName("weather") @Expose private List<Weather> weather; @SerializedName("total") @Expose private Integer total; @SerializedName("syncRequired") @Expose private Boolean syncRequired; @SerializedName("totalOfLocations") @Expose private Integer totalOfLocations; // Getters and Setters for all fields public List<Weather> getWeather() { return weather; } public void setWeather(List<Weather> weather) { this.weather = weather; } public Integer getTotal() { return total; } public void setTotal(Integer total) { this.total = total; } public Boolean isSyncRequired() { return syncRequired; } public void setSyncRequired(Boolean syncRequired) { this.syncRequired = syncRequired; } public Integer getTotalOfLocations() { return totalOfLocations; } public void setTotalOfLocations(Integer totalOfLocations) { this.totalOfLocations = totalOfLocations; } // Inner Weather class public static class Weather { @SerializedName("location") @Expose private String location; @SerializedName("condition") @Expose private String condition; // Getters and Setters public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public String getCondition() { return condition; } public void setCondition(String condition) { this.condition = condition; } } }
Step 2: Simplified JsonResponse Class
public class JsonResponse { @SerializedName("success") @Expose private Boolean success; @SerializedName("data") @Expose private Data data; // Getters and Setters public Boolean getSuccess() { return success; } public void setSuccess(Boolean success) { this.success = success; } public Data getData() { return data; } public void setData(Data data) { this.data = data; } }
Step 3: Parse and Handle Data
Gson gson = new Gson(); JsonResponse response = gson.fromJson(yourJsonString, JsonResponse.class); // Check which fields are present to determine response type if (response.getData().getWeather() != null) { // Handle weather response } else if (response.getData().isSyncRequired() != null) { // Handle sync response }
Which Approach to Choose?
- Approach 1 is better for type safety and scalability, especially if you expect more response types in the future. It keeps your data models clean and separated.
- Approach 2 is quicker to implement for simple cases where you only have two response types and don't mind checking for null fields.
内容的提问来源于stack exchange,提问作者Harith Haziq

