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

Jackson优化:如何仅序列化一次HeavyObject生成多份JSON

优化HeavyObject重复序列化的方案

问题背景

我有如下两个POJO:

public class ResponseA {
  private HeavyObject object;
  private AdditionalDataA additionalData;

  public ResponseA(HeavyObject object, AdditionalDataA additionalData) {
    this.object = object;
    this.additionalData = additionalData;
  }
}
public class ResponseB {
  private HeavyObject object;
  private AdditionalDataB additionalData;

  public ResponseB(HeavyObject object, AdditionalDataB additionalData) {
    this.object = object;
    this.additionalData = additionalData;
  }
}

还有一个向不同端点发送这些对象的方法:

public void sendResponses(
    ObjectMapper mapper, 
    HeavyObject object, 
    AdditionalDataA addDataA, 
    AdditionalDataB addDataB) {

  // 此处object被序列化两次,开销很高!
  String jsonA = mapper.writeValueAsString(new ResponseA(object, addDataA));
  String jsonB = mapper.writeValueAsString(new ResponseB(object, addDataB));
  sendResponseA(jsonA);
  sendResponseB(jsonB);
}

有没有办法实现序列化这些响应时,仅对HeavyObject执行一次序列化操作?

可行方案

1. 预序列化HeavyObject为JsonNode,再拼接其他数据

利用Jackson的JsonNode复用已序列化的结构,避免重复序列化HeavyObject:

public void sendResponses(
    ObjectMapper mapper, 
    HeavyObject object, 
    AdditionalDataA addDataA, 
    AdditionalDataB addDataB) throws JsonProcessingException {

  // 仅序列化HeavyObject一次
  JsonNode heavyObjectNode = mapper.valueToTree(object);

  // 构建ResponseA的JSON节点
  ObjectNode responseANode = mapper.createObjectNode();
  responseANode.set("object", heavyObjectNode);
  responseANode.set("additionalData", mapper.valueToTree(addDataA));
  String jsonA = mapper.writeValueAsString(responseANode);

  // 构建ResponseB的JSON节点
  ObjectNode responseBNode = mapper.createObjectNode();
  responseBNode.set("object", heavyObjectNode);
  responseBNode.set("additionalData", mapper.valueToTree(addDataB));
  String jsonB = mapper.writeValueAsString(responseBNode);

  sendResponseA(jsonA);
  sendResponseB(jsonB);
}

这种方式直接复用HeavyObject的JsonNode结构,后续仅做节点拼接,序列化开销大幅降低。

2. 预序列化HeavyObject为字符串,手动拼接JSON

如果不想依赖JsonNode,也可以先把HeavyObject序列化为字符串,再手动拼接成完整的响应JSON:

public void sendResponses(
    ObjectMapper mapper, 
    HeavyObject object, 
    AdditionalDataA addDataA, 
    AdditionalDataB addDataB) throws JsonProcessingException {

  String heavyObjectJson = mapper.writeValueAsString(object);
  String addDataAJson = mapper.writeValueAsString(addDataA);
  String addDataBJson = mapper.writeValueAsString(addDataB);

  // 拼接ResponseA的JSON
  String jsonA = String.format("{\"object\":%s,\"additionalData\":%s}", heavyObjectJson, addDataAJson);
  // 拼接ResponseB的JSON
  String jsonB = String.format("{\"object\":%s,\"additionalData\":%s}", heavyObjectJson, addDataBJson);

  sendResponseA(jsonA);
  sendResponseB(jsonB);
}

注意:Jackson会自动处理对象字段中的特殊字符转义,所以只要单个对象的序列化结果合法,拼接后的JSON也会是合法的。

3. 自定义序列化器(复杂场景适用)

如果需要对序列化流程做更多定制,可以为ResponseA和ResponseB实现自定义序列化器,复用预序列化的HeavyObject内容:

public class ResponseASerializer extends StdSerializer<ResponseA> {
  private final JsonNode preSerializedHeavyObject;

  public ResponseASerializer(JsonNode preSerializedHeavyObject) {
    super(ResponseA.class);
    this.preSerializedHeavyObject = preSerializedHeavyObject;
  }

  @Override
  public void serialize(ResponseA value, JsonGenerator gen, SerializerProvider provider) throws IOException {
    gen.writeStartObject();
    gen.writeFieldName("object");
    // 直接写入预序列化的节点内容
    gen.writeTree(preSerializedHeavyObject);
    gen.writeFieldName("additionalData");
    provider.defaultSerializeValue(value.getAdditionalData(), gen);
    gen.writeEndObject();
  }
}

使用时先预序列化HeavyObject,再为每个Response类型配置对应的序列化器:

public void sendResponses(
    ObjectMapper mapper, 
    HeavyObject object, 
    AdditionalDataA addDataA, 
    AdditionalDataB addDataB) throws JsonProcessingException {

  JsonNode heavyObjectNode = mapper.valueToTree(object);

  // 配置ResponseA的自定义序列化器
  ObjectMapper mapperA = mapper.copy();
  SimpleModule moduleA = new SimpleModule();
  moduleA.addSerializer(new ResponseASerializer(heavyObjectNode));
  mapperA.registerModule(moduleA);
  String jsonA = mapperA.writeValueAsString(new ResponseA(object, addDataA));

  // 同理配置ResponseB的自定义序列化器
  ObjectMapper mapperB = mapper.copy();
  SimpleModule moduleB = new SimpleModule();
  moduleB.addSerializer(new ResponseBSerializer(heavyObjectNode));
  mapperB.registerModule(moduleB);
  String jsonB = mapperB.writeValueAsString(new ResponseB(object, addDataB));

  sendResponseA(jsonA);
  sendResponseB(jsonB);
}

这种方式适合有特殊序列化需求的场景,但实现成本比前两种更高。


内容的提问来源于stack exchange,提问作者Matías Santurio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 16:48:31