Learn to create a custom serializer and custom deserializer for controlling the JSON to POJO conversion and vice versa using Jackson‘s StdSerializer and StdDeserializer classes.
For demo purposes, we will use the following Record and ArchiveStatus classes. We are using Lombok to reduce the boilerplate code such as getters, setters, constructors and toString() method.
By default, Jackson produces the JSON consisting of default fields structure and default value formats. For example, an instance of Record class will be serialized into the following format.
We want to customize the format to a custom format. The timestamp field should be used for display as received, and the status should be inline instead of a nested JSON field.
{"id":1,"message":"test-message","timestamp":"2022-01-01 01:00:01 AM GMT","status":"active"}
3. Creating Custom Serializer
Let us create a custom serializer according to our needs by extending StdSerializer class. The RecordSerializer‘s serialize() method will be invoked everytime Jackson needs to serialize its instance.
This class will create our desired JSON structure one field at a time. You can further customize the following code according to your needs. Note that DateTimeFormatter is a thread-safe object so we can reuse it with different threads.
Now, if we serialize the Record instance then we will get the output JSON in the desired format.
@TestvoidtestCustomSerialization()throwsJsonProcessingException{ZonedDateTime zdt =ZonedDateTime.of(LocalDateTime.of(2022,1,1,1,1),ZoneId.of("GMT"));Record record =newRecord(1L,"test-message", zdt,newArchiveStatus(true));JsonMapper jsonMapper =newJsonMapper();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. Custom Deserialization
The custom deserializer is created by extending the StdDeserializer class. Its deserialize() method receives the parsed JSON as a JsonNode instance. We need to fetch the specific JSON fields from the JsonNode and build the Record instance.
classRecordDeserializerextendsStdDeserializer<Record>{privatestaticDateTimeFormatter dtf
=DateTimeFormatter.ofPattern("yyyy-MM-dd hh:ss:mm a z",Locale.US);publicRecordDeserializer(){this(null);}publicRecordDeserializer(Class<?> vc){super(vc);}@OverridepublicRecorddeserialize(JsonParser parser,DeserializationContext ctx)throwsIOException,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 =newArchiveStatus(false);if(node.get("status")!=null){String active = node.get("status").asText();if("active".equalsIgnoreCase(active)){
status.setActive(true);}}returnnewRecord(id.longValue(), message,ZonedDateTime.parse(timestamp, dtf), status);}}
Now we can deserialize the custom JSON into the Record instance, as expected.
@TestvoidtestCustomDeserialization()throwsJsonProcessingException{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 =newJsonMapper();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. Registering Custom Serializer and Deserializer
Let’s check out the different ways to register the above create serializer and deserializer classes with Jackson runtime.
4.1. Using SimpleModule
The SimpleModule class registration of serializers and deserializers, bean serializer and deserializer modifiers, registration of subtypes and mix-ins, and some other commonly needed aspects.
When using annotations, we do not need to add the classes using SimpleModule registration process.
5. Conclusion
In this Jackson tutorial, we learned to create custom Jackson serializers and deserializers. We also learned to register the custom handlers using the SimpleModule as well as @JsonSerialize and @JsonDeserialize annotations.
A fun-loving family man, passionate about computers and problem-solving, with over 15 years of experience in Java and related technologies.
An avid Sci-Fi movie enthusiast and a fan of Christopher Nolan and Quentin Tarantino.
Comments