如何创建自定义的Jackson序列化器和反序列化器

6,329 阅读3分钟

学习使用JacksonStdSerializerStdDeserializer类创建一个自定义的序列器自定义的反序列器,以控制JSON到POJO的转换,反之亦然。

1.设置

添加最新版本的Jackson,如果你还没有把它添加到项目中。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>${jackson.version}</version>
</dependency>

为演示目的,我们将使用以下RecordArchiveStatus类。我们使用Lombok来减少模板代码,如getters、setters、构造函数和*toString()*方法。

@lombok.Data
@AllArgsConstructor
@NoArgsConstructor
class Record {

  private Long id;
  private String message;
  private ZonedDateTime timestamp;
  private ArchiveStatus status;
}

@lombok.Data
@AllArgsConstructor
@NoArgsConstructor
class ArchiveStatus {

  private Boolean active;
}

2.默认的序列化和要求

默认情况下,Jackson会生成由默认字段结构和默认值格式组成的JSON。例如,一个Record类的实例将被序列化为以下格式。

{
  "id" : 1,
  "message" : "test-message",
  "timestamp" : 1640998860.000000000,
  "status" : {
    "active" : true
  }
}

我们想将该格式定制为自定义格式。时间戳字段应该用于显示收到的信息,而状态应该是内联的,而不是嵌套的JSON字段。

{
	"id":1,
	"message":"test-message",
	"timestamp":"2022-01-01 01:00:01 AM GMT",
	"status":"active"
}

3.创建自定义序列器

让我们通过扩展StdSerializer类,根据我们的需要创建一个自定义的序列化器。RecordSerializerserialize() 方法将在每次Jackson需要序列化其实例时被调用。

这个类将一次创建我们所需的JSON结构的一个字段。你可以根据你的需要进一步定制下面的代码。注意,*DateTimeFormatter*是一个线程安全的对象,所以我们可以在不同的线程中重复使用它。

class RecordSerializer extends StdSerializer<Record> {

  private static DateTimeFormatter dtf
      = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:ss:mm a z", Locale.US);

  protected RecordSerializer() {
    super(Record.class);
  }

  @Override
  public void serialize(Record value, JsonGenerator gen,
                        SerializerProvider serializers) throws IOException {

    gen.writeStartObject();
    gen.writeNumberField("id", value.getId());
    gen.writeStringField("message", value.getMessage());
    gen.writeStringField("timestamp", dtf.format(value.getTimestamp()));
    if (value.getStatus() != null) {
      gen.writeStringField("status", value.getStatus().getActive() ?
          "active" : "inactive");
    }
    gen.writeEndObject();
  }
}

现在,如果我们将Record实例序列化,那么我们将得到所需格式的输出JSON。

@Test
void testCustomSerialization() throws JsonProcessingException {

  ZonedDateTime zdt = ZonedDateTime.of(LocalDateTime.of(2022, 1, 1, 1, 1), ZoneId.of("GMT"));
  Record record = new Record(1L, "test-message", zdt, new ArchiveStatus(true));

  JsonMapper jsonMapper = new JsonMapper();

  String json = jsonMapper.writerWithDefaultPrettyPrinter()
      .writeValueAsString(record);

  Assertions.assertEquals("{\"id\":1,\"message\":\"test-message\"," +
      "\"timestamp\":\"2022-01-01 01:00:01 AM GMT\",\"status\":\"active\"}" , json);
}

4.自定义反序列化

自定义反序列化器是通过扩展StdDeserializer类创建的。它的deserialize() 方法接收解析后的JSON作为一个JsonNode实例。我们需要从JsonNode中获取特定的JSON字段并建立记录实例。

class RecordDeserializer extends StdDeserializer<Record> {

  private static DateTimeFormatter dtf 
  		= DateTimeFormatter.ofPattern("yyyy-MM-dd hh:ss:mm a z", Locale.US);

  public RecordDeserializer() {
    this(null);
  }

  public RecordDeserializer(Class<?> vc) {
    super(vc);
  }

  @Override
  public Record deserialize(JsonParser parser, DeserializationContext ctx)
      throws IOException, JacksonException {

    JsonNode node = parser.getCodec().readTree(parser);
    Integer id = (Integer) ((IntNode) node.get("id")).numberValue();
    String message = node.get("message").asText();
    String timestamp = node.get("timestamp").asText();
    ArchiveStatus status = new ArchiveStatus(false);

    if(node.get("status") != null) {
      String active = node.get("status").asText();
      if("active".equalsIgnoreCase(active)) {
        status.setActive(true);
      }
    }

    return new Record(id.longValue(), message, ZonedDateTime.parse(timestamp, dtf), status);
  }
}

现在我们可以按照预期将自定义JSON反序列化到Record实例中。

@Test
void testCustomDeserialization() throws JsonProcessingException {
  ZonedDateTime zdt = ZonedDateTime
      .of(LocalDateTime.of(2022, 1, 1, 1, 1), ZoneId.of("GMT"));

  String json = "{\"id\":1,\"message\":\"test-message\"," +
      "\"timestamp\":\"2022-01-01 01:00:01 AM GMT\",\"status\":\"active\"}";

  JsonMapper jsonMapper = new JsonMapper();
  Record record = jsonMapper.readValue(json, Record.class);

  Assertions.assertEquals(1L, record.getId());
  Assertions.assertEquals("test-message", record.getMessage());
  Assertions.assertEquals(zdt, record.getTimestamp());
  Assertions.assertEquals(true, record.getStatus().getActive());
}

4.注册自定义序列化器和反序列化器

让我们看看有哪些不同的方法可以将上述创建的序列化器和反序列化器类注册到Jackson运行时。

4.1.使用SimpleModule

SimpleModule类注册序列化器和反序列化器,bean序列化器和反序列化器的修改器,注册子类型和混合器,以及其他一些常用的方面。

JsonMapper jsonMapper = new JsonMapper();

SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Record.class, new RecordSerializer());
simpleModule.addDeserializer(Record.class, new RecordDeserializer());

jsonMapper.registerModule(simpleModule);

4.2.使用@JsonSerialize和@JsonDeserialize

另一种注册自定义处理程序的有效方法是使用*@JsonSerialize@JsonDeserialize*注解,如下所示。

@JsonSerialize(using = RecordSerializer.class)
@JsonDeserialize(using = RecordDeserializer.class)
class Record {
	...
}

当使用注解时,我们不需要使用SimpleModule注册过程添加类。

5.总结

在这个Jackson教程中,我们学习了如何创建自定义的Jackson序列化器和反序列化器。我们还学会了使用SimpleModule以及@JsonSerialize和@JsonDeserialize注解来注册自定义处理程序。