如何将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
相关产品推荐
相关产品推荐

