如何实现Gson逐字段解析日志并定位解析错误字段?
1. How to Log Field Names and Corresponding Values with Gson
You’ve got two reliable approaches here—pick one based on whether you want a generic solution that works for any model class, or a class-specific implementation with more parsing control:
Option 1: Generic Reflection-Based Approach
This method works for any model annotated with @SerializedName, no need to write custom code per class:
// First parse your JSON into the Item object Gson gson = new Gson(); String jsonResponse = "{ \"ItemCode\":\"A12\", \"ItemSN\":\"123455672\", \"ItemDesc\":\"Google Pixel\" }"; Item item = gson.fromJson(jsonResponse, Item.class); // Use reflection to iterate over fields with @SerializedName Field[] fields = Item.class.getDeclaredFields(); for (Field field : fields) { SerializedName serializedName = field.getAnnotation(SerializedName.class); if (serializedName != null) { field.setAccessible(true); // Bypass private field access restrictions try { Object fieldValue = field.get(item); System.out.printf("%s is %s%n", serializedName.value(), fieldValue); } catch (IllegalAccessException e) { // Log or handle the exception as needed e.printStackTrace(); } } }
This will output exactly what you need:
ItemCode is A12
ItemSN is 123455672
ItemDesc is Google Pixel
Option 2: Custom TypeAdapter for Class-Specific Logging
If you want to log fields as they’re parsed (instead of after full object creation), create a custom TypeAdapter for your Item class:
public class ItemTypeAdapter extends TypeAdapter<Item> { private final Gson gson = new Gson(); @Override public void write(JsonWriter out, Item value) throws IOException { // Reuse Gson's default serialization logic if needed gson.getAdapter(Item.class).write(out, value); } @Override public Item read(JsonReader in) throws IOException { Item item = new Item(); in.beginObject(); while (in.hasNext()) { String fieldName = in.nextName(); switch (fieldName) { case "ItemCode": String itemCode = in.nextString(); item.setItemCode(itemCode); System.out.println("ItemCode is " + itemCode); break; case "ItemSN": String itemSN = in.nextString(); item.setItemSN(itemSN); System.out.println("ItemSN is " + itemSN); break; case "ItemDesc": String itemDesc = in.nextString(); item.setItemDesc(itemDesc); System.out.println("ItemDesc is " + itemDesc); break; default: in.skipValue(); // Skip unknown fields to avoid parsing errors break; } } in.endObject(); return item; } }
Register this adapter with your GsonBuilder:
Gson gson = new GsonBuilder() .registerTypeAdapter(Item.class, new ItemTypeAdapter()) .create();
2. Troubleshooting NumberFormatException in Retrofit-Gson-RxJava Setup
Pinpointing invalid numeric fields in large, nested responses is tough—but these fixes will make it straightforward:
Option 1: Custom Safe Number TypeAdapterFactory
Create a factory that wraps Gson’s default number parsers and adds detailed context to NumberFormatException messages. This will tell you exactly which field (including nested paths) has invalid data:
public class SafeNumberTypeAdapterFactory implements TypeAdapterFactory { @SuppressWarnings("unchecked") @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { Class<T> rawType = (Class<T>) type.getRawType(); // Only target numeric types (primitives and wrapper classes) boolean isNumeric = Number.class.isAssignableFrom(rawType) || rawType == int.class || rawType == long.class || rawType == float.class || rawType == double.class; if (!isNumeric) { return null; } final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type); return new TypeAdapter<T>() { @Override public void write(JsonWriter out, T value) throws IOException { delegate.write(out, value); } @Override public T read(JsonReader in) throws IOException { try { return delegate.read(in); } catch (NumberFormatException e) { // Get the full path of the problematic field (e.g., "data.orders[3].total") String fieldPath = in.getPath(); // Capture the invalid string value causing the error String invalidValue = in.nextString(); // Throw a new exception with clear debugging context throw new NumberFormatException(String.format( "Failed to parse numeric field '%s' with invalid value: '%s'", fieldPath, invalidValue )); } } }; } }
Register this factory with your GsonBuilder:
Gson gson = new GsonBuilder() .registerTypeAdapterFactory(new SafeNumberTypeAdapterFactory()) .create();
Now when the error occurs, your exception log will include the exact field path and invalid value, making it trivial to locate the issue.
Option 2: Log Full Response Body with Retrofit Interceptor
Add an OkHttp interceptor to log the raw response body. This lets you cross-reference the error message with the actual JSON:
public class ResponseLoggingInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response response = chain.proceed(request); // Read and log the response body (note: string() can only be called once, so rebuild the response) String responseBody = response.body().string(); Log.d("RetrofitRawResponse", "Response for " + request.url() + ": " + responseBody); // Rebuild the response to pass it along to Retrofit return response.newBuilder() .body(ResponseBody.create(response.body().contentType(), responseBody)) .build(); } }
Add this interceptor to your OkHttpClient:
OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(new ResponseLoggingInterceptor()) .build();
Then use this client in your Retrofit setup:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("your_base_url") .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create(gson)) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build();
When the NumberFormatException hits, pull the raw JSON from logs and use the field path from the custom exception to find the problematic value instantly.
内容的提问来源于stack exchange,提问作者DCoder

