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

如何用Retrofit2解析每行一个JSON对象的流式API响应?

解决Retrofit2解析每行独立JSON对象流式API的问题

你的问题根源在于:API返回的是每行一个独立JSON对象的流格式,而非标准的JSON数组,Gson默认转换器仅能处理单个JSON对象或完整JSON数组,因此会出现解析错误。以下两种方案可实现自动转换为对象列表:

方案一:自定义Retrofit ConverterFactory(推荐)

通过自定义转换器实现逐行读取流并解析JSON对象,最终返回对象列表,适配Retrofit的调用逻辑。

1. 实现自定义Converter和ConverterFactory

public class LineDelimitedGsonConverterFactory extends Converter.Factory {
    private final Gson gson;

    public static LineDelimitedGsonConverterFactory create(Gson gson) {
        return new LineDelimitedGsonConverterFactory(gson);
    }

    private LineDelimitedGsonConverterFactory(Gson gson) {
        this.gson = gson;
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        // 仅处理List类型的返回值
        if (!(type instanceof ParameterizedType) || ((ParameterizedType) type).getRawType() != List.class) {
            return super.responseBodyConverter(type, annotations, retrofit);
        }
        Type itemType = ((ParameterizedType) type).getActualTypeArguments()[0];
        return new LineDelimitedResponseBodyConverter<>(gson, itemType);
    }

    private static class LineDelimitedResponseBodyConverter<T> implements Converter<ResponseBody, List<T>> {
        private final Gson gson;
        private final Type itemType;

        public LineDelimitedResponseBodyConverter(Gson gson, Type itemType) {
            this.gson = gson;
            this.itemType = itemType;
        }

        @Override
        public List<T> convert(ResponseBody value) throws IOException {
            List<T> result = new ArrayList<>();
            BufferedReader reader = new BufferedReader(new InputStreamReader(value.byteStream()));
            String line;
            try {
                while ((line = reader.readLine()) != null) {
                    // 跳过空行
                    if (line.trim().isEmpty()) continue;
                    T item = gson.fromJson(line, itemType);
                    result.add(item);
                }
            } finally {
                value.close();
            }
            return result;
        }
    }
}

2. 修改Retrofit配置

将自定义转换器放在GsonConverterFactory之前,确保优先处理流式响应:

Gson gson = new GsonBuilder()
        .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
        .create();

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(okHttpClient)
        .addConverterFactory(LineDelimitedGsonConverterFactory.create(gson))
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build();

3. 修改API接口定义

直接声明返回Call<List<Response>>:

public interface Api {
    @Streaming
    @POST("/stream")
    @Headers({"accept: application/stream+json", "content-type: application/json"})
    Call<List<Response>> generateStream2(@Body Request request);
}

方案二:手动处理ResponseBody流

若不想自定义转换器,可直接在接口中返回ResponseBody,在回调内手动逐行解析:

1. 修改API接口

public interface Api {
    @Streaming
    @POST("/stream")
    @Headers({"accept: application/stream+json", "content-type: application/json"})
    Call<ResponseBody> generateStream2(@Body Request request);
}

2. 调用时处理流

api.generateStream2(request).enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        if (!response.isSuccessful()) {
            // 处理请求错误
            return;
        }
        BufferedReader reader = new BufferedReader(new InputStreamReader(response.body().byteStream()));
        List<Response> responseList = new ArrayList<>();
        String line;
        try {
            while ((line = reader.readLine()) != null) {
                if (line.trim().isEmpty()) continue;
                Response item = gson.fromJson(line, Response.class);
                responseList.add(item);
            }
            // 处理解析完成的对象列表
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                response.body().close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        // 处理请求失败
    }
});

关键注意事项

  • 必须保留@Streaming注解,避免OkHttp将整个流加载到内存,处理大流量响应时更高效。
  • 自定义转换器方案更贴合Retrofit的设计逻辑,无需在业务代码中重复编写流解析逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 07:10:26