1. Introduction
In this article, we’ll see how we can serialize and deserialize java.sql.Blob using Jackson. The java.sql.Blob represents a Binary Large Object (Blob) in Java, which can store large amounts of binary data. When dealing with JSON serialization and deserialization using Jackson, handling Blob objects can be tricky since Jackson does not support them directly. However, we can create custom serializers and deserializers to handle Blob objects.
We’ll start with setting up the environment and a simple example. Further along, we’ll quickly show how we can implement a custom serializer and deserialize for Blob data type. Finally, we’ll verify our approach with the tests using our simple example use case.
2. Dependency and Example Setup
Firstly, let’s ensure we have the necessary jackson-databind dependency in our pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
We’ll next demonstrate how to integrate Blob fields within a typical POJO, highlighting the need for custom serialization and deserialization. Let’s create a simple User POJO that contains an ID, name, and profilePicture which is of type Blob:
public class User {
private int id;
private String name;
private Blob profilePicture;
//Constructor
// Getters and setters
}
We’ll later use this User class to demonstrate the custom serializing and deserializing involving a Blob field.
3. Defining Blob Serializer
Let’s define a serializer that will convert the profilePicture attribute of the User to a Base64-encoded binary string:
@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);
}
}
}
Importantly*, @JacksonStdImpl i*ndicates that this class is a standard implementation of a serializer that Jackson can use. It’s a marker annotation typically used for built-in serializers and deserializers in Jackson.
*Our SqlBlobSerializer extends JsonSerialzier
Essentially, the serialize method converts the Blob into a byte array using getBytes(). It then writes the byte array as a Base64-encoded binary string using gen.writeBinary()
5. Defining Blob Deserializer
Let’s now define a deserializer, which can convert a Base64 encoded string to a Blob using Jackson:
@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);
}
}
}
Here, SqlBlobDeserializer extends JsonDeserializer
*Essentially, the SqlBlobDeserializer retrieves binary data into byte[] from the JSON using getBinaryValue().* It then converts the byte array into a SerialBlob object that is an implementation of java.sql.Blob.
6. Registering the Custom Serializer and Deserializer
Now that we have a BlobSerializer and BlobDeserializer, the next step is to register them both with Jackson. Registering custom serializers and deserializers with Jackson means configuring the Jackson ObjectMapper to use specific classes for converting certain types of Java objects to and from JSON. Let’s create a SimpleModule next and add our blobSerializer and blobDeserializer to this module:
SimpleModule module = new SimpleModule();
module.addSerializer(Blob.class, new SqlBlobSerializer());
module.addDeserializer(Blob.class, new SqlBlobDeserializer());
Next, let’s create an ObjectMapper, and register this module to it:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Essentially, by registering a specific module to the ObjectMapper, we make sure that it knows how to handle non-standard types during JSON processing. In this case, we are ensuring our ObjectMapper knows how to handle Blob type using our custom serializer and deserializer.
7. Unit Test
Finally, let’s see our registered serializer and deserializer in action by writing some unit tests. Let’s test the BlobSerializer first*:*
@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);
}
The test verifies that the serialized JSON string matches the expected JSON format. Specifically, the profilePicture field in the JSON is expected to be a base64-encoded string representing the Blob data.
Next, let’s write a test for 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);
}
Here, the Blob data is expected to match the original byte data for the string “example data”. This test ensures that the custom SqlBlobDeserialiser correctly converts the base64-encoded string back into a Blob object, preserving the original binary data within the User object.
8. Conclusion
In this article, we demonstrated how to effectively serialize and deserialize java.sql.Blob objects using the Jackson library in Java. We created custom serializers and deserializers to handle the binary data within the Blob objects, converting them to and from base64-encoded strings in JSON format.
As always, the full implementation of this article can be found over on GitHub.