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

如何将Apache、OkHttp的HttpClient响应对象序列化与反序列化为字节数组

问题描述

需要对Apache HttpClient、OkHttp的HTTP响应对象进行序列化和反序列化操作,但尝试以下方法均失败:

  • Java Serializable接口:这两个客户端的响应对象都未实现Serializable接口,无法直接序列化。
  • Jackson:响应对象没有无参构造函数,反序列化时抛出错误:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.example.Student` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
  • Gson:序列化时出现内存溢出(以Apache HttpClient为例):
    序列化代码:
    Gson gson = new GsonBuilder().setPrettyPrinting().create();
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    OutputStreamWriter writer = new OutputStreamWriter(outputStream);
    gson.toJson(httpResponse, writer);
    writer.flush();
    
    反序列化代码:
    byte[] data = outputStream.toByteArray();
    ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
    InputStreamReader reader = new InputStreamReader(inputStream);
    HttpResponse res = gson.fromJson(reader, HttpResponse.class);
    System.out.println("HttpResponse-> " + EntityUtils.toString(res.getEntity()));
    
    错误信息:
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOf(Arrays.java:3332)
        at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
        at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
        at java.lang.StringBuffer.append(StringBuffer.java:276)
        at java.io.StringWriter.write(StringWriter.java:101)
        at com.google.gson.stream.JsonWriter.newline(JsonWriter.java:602)
        at com.google.gson.stream.JsonWriter.beforeValue(JsonWriter.java:646)
        at com.google.gson.stream.JsonWriter.value(JsonWriter.java:532)
        at com.google.gson.internal.bind.TypeAdapters$5.write(TypeAdapters.java:189)
        at com.google.gson.internal.bind.TypeAdapters$5.write(TypeAdapters.java:173)
        at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
        at com.google.gson.internal.bind.ArrayTypeAdapter.write(ArrayTypeAdapter.java:95)
        at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
        at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
        at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
        at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
        at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
        at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:97)
        at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61)
        at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
        at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
    
解决方案

直接序列化Apache/OkHttp的原始响应对象之所以出问题,核心是这些对象内部包含复杂引用链(比如连接池、输入流、回调引用等),既不适合序列化,也没做序列化设计。正确做法是自定义数据传输对象(DTO),只保留需要的响应数据,再对DTO进行序列化/反序列化。

步骤1:定义响应DTO

以Apache HttpClient的HttpResponse为例,提取关键字段:

public class HttpResponseDTO {
    private int statusCode;
    private String statusText;
    private Map<String, List<String>> headers;
    private String body;

    // 无参构造函数
    public HttpResponseDTO() {}

    // 全参构造函数
    public HttpResponseDTO(int statusCode, String statusText, Map<String, List<String>> headers, String body) {
        this.statusCode = statusCode;
        this.statusText = statusText;
        this.headers = headers;
        this.body = body;
    }

    // 自行补充getter、setter方法
}

步骤2:将原始响应转换为DTO

// Apache HttpResponse转DTO
public static HttpResponseDTO convertToDto(HttpResponse httpResponse) throws IOException {
    int statusCode = httpResponse.getStatusLine().getStatusCode();
    String statusText = httpResponse.getStatusLine().getReasonPhrase();
    
    // 提取响应头
    Map<String, List<String>> headers = new HashMap<>();
    Header[] allHeaders = httpResponse.getAllHeaders();
    for (Header header : allHeaders) {
        headers.computeIfAbsent(header.getName(), k -> new ArrayList<>()).add(header.getValue());
    }
    
    // 提取响应体(大文件场景建议用流式处理,避免OOM)
    String body = EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8);
    
    return new HttpResponseDTO(statusCode, statusText, headers, body);
}

// OkHttp Response转DTO
public static HttpResponseDTO convertToDto(okhttp3.Response okHttpResponse) throws IOException {
    int statusCode = okHttpResponse.code();
    String statusText = okHttpResponse.message();
    
    // 提取响应头
    Map<String, List<String>> headers = new HashMap<>();
    okHttpResponse.headers().names().forEach(name -> {
        headers.put(name, okHttpResponse.headers().values(name));
    });
    
    // 提取响应体
    String body = okHttpResponse.body().string();
    
    return new HttpResponseDTO(statusCode, statusText, headers, body);
}

步骤3:对DTO进行序列化/反序列化

使用Jackson

DTO有无参构造函数,Jackson可直接处理:

ObjectMapper objectMapper = new ObjectMapper();

// 序列化
HttpResponseDTO dto = convertToDto(httpResponse);
String json = objectMapper.writeValueAsString(dto);

// 反序列化
HttpResponseDTO deserializedDto = objectMapper.readValue(json, HttpResponseDTO.class);

使用Gson

DTO结构简单无循环引用,不会出现OOM:

Gson gson = new GsonBuilder().setPrettyPrinting().create();

// 序列化
HttpResponseDTO dto = convertToDto(httpResponse);
String json = gson.toJson(dto);

// 反序列化
HttpResponseDTO deserializedDto = gson.fromJson(json, HttpResponseDTO.class);

步骤4:(可选)从DTO还原为原始响应对象

若需要将DTO转回客户端响应对象,可通过客户端Builder创建,以Apache HttpClient为例:

public static HttpResponse convertToOriginal(HttpResponseDTO dto) {
    BasicHttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), dto.getStatusCode(), dto.getStatusText());
    
    // 设置响应头
    dto.getHeaders().forEach((name, values) -> {
        values.forEach(value -> response.addHeader(name, value));
    });
    
    // 设置响应体
    response.setEntity(new StringEntity(dto.getBody(), StandardCharsets.UTF_8));
    
    return response;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 13:06:29