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

Retrofit无法自动解析内部JSON对象问题求助

Retrofit自动解析嵌套JSON对象失败的解决问题

问题说明

原本定义的ProfileResponse类中,header、tests、entries字段对应嵌套对象/数组,但Retrofit无法自动解析导致请求失败;将这些字段改为String类型后,手动用Gson解析可正常工作,希望实现Retrofit自动完整解析ProfileResponse对象。


相关代码片段

原问题模型类(解析失败版本)

public class ProfileResponse {
@SerializedName("result")
private int result;

@SerializedName("comment")
private String comment;

@SerializedName("time")
private String time;

@SerializedName("header")
private Header header;

@SerializedName("tests")
private Test[] tests;

@SerializedName("entries")
private Entry[] entries;
}

临时可行模型类(需手动解析)

public class ProfileResponse {
@SerializedName("result")
private int result;

@SerializedName("comment")
private String comment;

@SerializedName("time")
private String time;

@SerializedName("header")
private String header;

@SerializedName("tests")
private String tests;

@SerializedName("entries")
private String entries;
}

手动解析代码片段

String testString = profileDataString.getTests();
Test[] tests = gson.fromJson(testString, Test[].class);

嵌套对象模型类

public class Test {
@SerializedName("date")
private String date;

@SerializedName("lessonNo")
private int lessonNo;

@SerializedName("testNo")
private int testNo;

@SerializedName("correct")
private int correctAnswers;

@SerializedName("wrong")
private int wrongAnswers;
// getters
}

public class Entry {
@SerializedName("subjectID")
private int subjectID;

@SerializedName("commentID")
private int commentID;

@SerializedName("comment")
private String comment;

@SerializedName("dateTS")
private String dateTime;

@SerializedName("usercode")
private int userCode;

@SerializedName("nickname")
private String nickName;

@SerializedName("likepoint")
private int likePoint;

@SerializedName("dislikepoint")
private int dislikePoint;

@SerializedName("subjectName")
private String subjectName;
// getters
}

public class Header {
@SerializedName("nickname")
private String nickName;

@SerializedName("profilephoto")
private String profilePhoto;

@SerializedName("totalTests")
private int totalTests;

@SerializedName("totalChallenges")
private int totalChallenges;

@SerializedName("totalpoints")
private int totalPoints;

@SerializedName("socialearned")
private int socialEarned;
// getters
}

Retrofit单例类

public class ApiClientRetrofit {
private static Retrofit retrofit = null;

public static Retrofit getInstance() {
    if (retrofit == null) {
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(httpLoggingInterceptor)
                .build();

        retrofit = new Retrofit.Builder()
                .baseUrl(SERVER_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .build();
    }
    return retrofit;
}
}

请求代码

Retrofit retrofit=ApiClientRetrofit.getInstance();
ServerApiInterface service=retrofit.create(ServerApiInterface.class);
Call<ProfileResponse> call = service.loadProfile(101,0,0,10,10,1);
call.enqueue(new Callback<ProfileResponse>() {
    @Override
    public void onResponse(Call<ProfileResponse> call, Response<ProfileResponse> response) {
        if(response.isSuccessful()){
            Log.i(TAG, "onResponse: response is successful");
        }
    }

    @Override
    public void onFailure(Call<ProfileResponse> call, Throwable t) {
        Log.i(TAG, "onFailure: ");
    }
});

请求接口定义

public interface ServerApiInterface {
@FormUrlEncoded
@POST(ServerAdress.PROFILE_PHP)
Call<ProfileResponse> loadProfile(@Field("usercode") int userCode,
                                        @Field("testStart") int testStart,
                                        @Field("entryStart") int entryStart,
                                        @Field("entryCount") int entryCount,
                                        @Field("testCount") int testCount,
                                        @Field("header") int header);
}

解决方法

问题根源是服务器返回的header、tests、entries字段是JSON字符串格式(而非直接的JSON对象/数组),默认Gson无法自动将JSON字符串解析为对应对象,需自定义TypeAdapter处理:

步骤1:创建自定义TypeAdapterFactory

public class StringToObjectAdapterFactory implements TypeAdapterFactory {
    @SuppressWarnings("unchecked")
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        final TypeAdapter<String> stringAdapter = gson.getAdapter(String.class);

        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 {
                if (in.peek() == JsonToken.STRING) {
                    String jsonStr = stringAdapter.read(in);
                    return gson.fromJson(jsonStr, type.getType());
                }
                return delegate.read(in);
            }
        };
    }
}

步骤2:配置Retrofit使用自定义Gson

修改ApiClientRetrofit类,初始化带自定义Adapter的Gson:

public class ApiClientRetrofit {
private static Retrofit retrofit = null;

public static Retrofit getInstance() {
    if (retrofit == null) {
        Gson customGson = new GsonBuilder()
                .registerTypeAdapterFactory(new StringToObjectAdapterFactory())
                .create();

        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(httpLoggingInterceptor)
                .build();

        retrofit = new Retrofit.Builder()
                .baseUrl(SERVER_URL)
                .addConverterFactory(GsonConverterFactory.create(customGson))
                .client(okHttpClient)
                .build();
    }
    return retrofit;
}
}

步骤3:恢复原ProfileResponse类

将ProfileResponse改回最初版本,字段类型使用Header、Test[]、Entry[]即可,无需手动解析。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 06:10:30