使用 Jackson 序列化和反序列化 java.sql.Blob

在本文中,我们将了解如何使用 Jackson 序列化和反序列化java.sql.Blob。

java.sql.Blob表示Java中的二进制大对象(Blob),它可以存储大量二进制数据。使用 Jackson 进行 JSON 序列化和反序列化时, 处理Blob对象可能很 棘手,因为 Jackson 不直接支持它们。但是,我们可以创建自定义序列化器和反序列化器来处理Blob 对象。

我们将从设置环境和一个简单的示例开始。接下来,我们将快速展示如何实现自定义序列化程序并反序列化Blob数据类型。最后,我们将使用简单的示例用例通过测试验证我们的方法。

依赖项和示例设置
首先,让我们确保我们的pom.xml中具有必要的jackson-databind 依赖项:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.3</version>
</dependency>

接下来,我们将演示如何在典型的 POJO 中集成Blob 字段,重点介绍自定义序列化和反序列化的需求。让我们创建一个简单的User POJO,其中包含ID、name和Blob类型的 profilePicture :

public class User {
    private int id;
    private String name;
    private Blob profilePicture;
    //Constructor 
   
// Getters and setters
}

稍后我们将使用这个User类来演示涉及Blob字段的自定义序列化和反序列化。

定义Blob序列化器
让我们定义一个序列化器,将用户的profilePicture属性转换为 Base64 编码的二进制字符串:

@JacksonStdImpl
public class SqlBlobSerializer extends JsonSerializer<Blob> {
    @Override
    public void serialize(Blob value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        try {
            byte[] blobBytes = value.getBytes(1, (int) value.length());
            gen.writeBinary(blobBytes);
        } catch (Exception e) {
            throw new IOException("Failed to serialize Blob", e);
        }
    }
}

重要的是,@JacksonStdImpl表示此类是 Jackson 可以使用的序列化器的标准实现。它是 Jackson 中内置序列化器和反序列化器通常使用的标记注释。

我们的 SqlBlobSerializer 扩展了JsonSerialzier,这是 Jackson 提供的用于定义自定义序列化器的通用类。我们重写了 serialize 方法,传递要序列化的 Blob对象以及JsonGenerator 和 SerializerProvider。JsonGenerator用于生成结果JSON内容,而SerializerProvider用于提供序列化器以序列化对象

本质上,  serialize方法使用getBytes()将Blob转换为字节数组。然后使用 gen.writeBinary()将字节数组写入 Base64 编码的二进制字符串

定义Blob反序列化器
现在让我们定义一个反序列化器,它可以使用 Jackson 将Base64编码的字符串转换为Blob:

@JacksonStdImpl
public class SqlBlobDeserializer extends JsonDeserializer<Blob> {
    @Override
    public Blob deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        try {
            byte[] blobBytes = p.getBinaryValue();
            return new SerialBlob(blobBytes);
        } catch (Exception e) {
            throw new IOException("Failed to deserialize Blob", e);
        }
    }
}

这里,SqlBlobDeserializer 扩展了JsonDeserializer,这是 Jackson 提供的用于定义自定义反序列化器的通用类。然后,我们从JsonDeserializer中重写反序列化方法,并传递JsonParser,它是用于读取 JSON 内容的解析器。此外,我们传递了可用于访问有关反序列化过程的信息的DeserializationContext 。

本质上,SqlBlobDeserializer使用getBinaryValue()从 JSON 中检索二进制数据并将其放入byte[]中。 然后,它将字节数组转换为SerialBlob 对象,该对象是 java.sql.Blob 的实现。

注册自定义序列化器和反序列化器
现在我们有了BlobSerializer和BlobDeserializer,下一步就是将它们都注册到 Jackson 中。使用 Jackson 注册自定义序列化器和反序列化器意味着配置 Jackson ObjectMapper 以使用特定类将某些类型的 Java 对象转换为 JSON 或从 JSON 转换为 Java 对象。接下来让我们创建一个SimpleModule并将我们的blobSerializer和blobDeserializer添加到此模块:

SimpleModule module = new SimpleModule();
module.addSerializer(Blob.class, new SqlBlobSerializer());
module.addDeserializer(Blob.class, new SqlBlobDeserializer());

接下来,让我们创建一个ObjectMapper,并将这个模块注册到它:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);

本质上,通过向 ObjectMapper 注册特定模块,我们可以确保它知道如何在 JSON 处理期间处理非标准类型。在本例中,我们确保ObjectMapper知道如何使用自定义序列化器和反序列化器处理Blob类型。

单元测试
最后,让我们通过编写一些单元测试来查看我们注册的序列化器和反序列化器的实际运行情况。我们先测试一下BlobSerializer :

@Test
public void givenUserWithBlob_whenSerialize_thenCorrectJsonDataProduced() throws Exception {
    User user = new User();
    user.setId(1);
    user.setName("Test User");
   
//sample blob data from byte[] 
    byte[] profilePictureData =
"example data".getBytes();
    Blob profilePictureBlob = new SerialBlob(profilePictureData);
    user.setProfilePicture(profilePictureBlob);
    String json = mapper.writeValueAsString(user);
    String expectedJson =
"{\"id\":1,\"name\":\"Test User\",\"profilePicture\":\"ZXhhbXBsZSBkYXRh\"}";
    assertEquals(expectedJson, json);
}

该测试验证序列化的 JSON 字符串是否符合预期的 JSON 格式。具体来说, JSON 中的profilePicture 字段应为表示Blob 数据的 base64 编码字符串。

接下来我们来为BlobDeserializer编写一个测试:

@Test
public void givenUserJsonWithBlob_whenDeserialize_thenCorrectDataRecieved() throws Exception {
    String json = "{\"id\":1,\"name\":\"Test User\",\"profilePicture\":\"ZXhhbXBsZSBkYXRh\"}";
    User deserializedUser = mapper.readValue(json, User.class);
    assertEquals(1, deserializedUser.getId());
    assertEquals(
"John Doe", deserializedUser.getName());
    byte[] expectedProfilePictureData =
"example data".getBytes();
    Blob deserializedProfilePictureBlob = deserializedUser.getProfilePicture();
    byte[] deserializedData = deserializedProfilePictureBlob.getBytes(1, (int) deserializedProfilePictureBlob.length());
    assertArrayEquals(expectedProfilePictureData, deserializedData);
}

这里,Blob数据应该与字符串“example data”的原始字节数据匹配。此测试确保自定义SqlBlobDeserialiser 正确地将 base64 编码的字符串转换回Blob对象,并保留User对象 内的原始二进制数据。