Skip to content

Custom Serializers and Deserializers

Martin Ledvinka edited this page Jun 7, 2024 · 4 revisions

Custom Serializers

Since 0.8.7

Custom serializers allow adjusting the serialization of specific types in case the default is not suitable.

Custom serializers must implement the ValueSerializer<T> interface which has only one method - serialize. This method takes two arguments:

  • The instance to be serialized
  • a SerializationContext instance representing the current serialization context

and returns a JSONNode representing the serialized value. The serialization context contains some important contextual data, like the field being serialized (if available) and the IRI of the property that this attribute represents (again, if available). Note that, for example, if the value being serialized is an element of a collection, the field and the attribute IRI may not be available.

Example

Here's a simplistic example that demonstrates how custom serializer for LocalDate can be implemented to serialize instances as ISO-formatted string node.

public class LocalDateSerializer implements ValueSerializer<LocalDate> {
    @Override
    public JsonNode serialize(LocalDate value, SerializationContext<LocalDate> ctx) {
        return JsonNodeFactory.createLiteralNode(ctx.getAttributeId(), value.toString());
    }
}

Of course, such a simple serializer could have been declared as a lambda.

Serializer instances are registered on the JsonLdSerializer for specific types. So, for example:

sut = JsonLdSerializer.createContextBuildingJsonLdSerializer(new JacksonJsonWriter());
sut.registerSerializer(LocalDate.class, new LocalDateSerializer());

JB4JSON-LD will use the same instance, so they should have no state.

Custom Deserializers

Note that in 0.14.0 the library was rewritten to use the Jakarta JSON API. With that, the ValueDeserializer API has changed as well.

Custom deserializers allow adjusting the deserialization of specific types in case the default is not suitable.

Custom deserializers must implement the ValueDeserializer<T> interface which has only one method - deserialize. This method takes two arguments:

  • The JSON-LD node to be deserialized. It is represented by the Jakarta JSON JsonValue interface, but given the nature of data (using expanded JSON-LD), it will more likely be JsonObject.
  • a DeserializationContext instance representing the current deserialization context.

The method should return the deserialized value.

Note that since the deserialization first expands the JSON-LD input, the JSON-LD node passed to the custom deserializer will be expanded as well. This in particular means that the values of the map will be always lists (unless they represent the @id attribute, which is always singular).

Example

A simplistic example of a custom deserializer (taken from the tests) may look as follows:

public class EmployeeDeserializer implements ValueDeserializer<Employee> {
        @Override
        public Employee deserialize(JsonValue jsonNode, DeserializationContext<Employee> ctx) {
            final Employee result = new Employee();
            result.setUri(URI.create(jsonNode.asJsonObject().getString(JsonLd.ID)));
            final JsonArray firstName = jsonNode.asJsonObject().get(Vocabulary.FIRST_NAME).asJsonArray();
            result.setFirstName(firstName.get(0).asJsonObject().getString(JsonLd.VALUE));
            final JsonArray lastName = jsonNode.asJsonObject().get(Vocabulary.LAST_NAME).asJsonArray();
            result.setLastName(lastName.get(0).asJsonObject().getString(JsonLd.VALUE));
            return result;
        }
    }

Deserializer instances are registered on the JsonLdDeserializer for specific types. So, for example:

final JsonLdDeserializer deserializer = JsonLdDeserializer.createExpandedDeserializer(config);
deserializer.registerDeserializer(Employee.class, new EmployeeDeserializer());

JB4JSON-LD will use the same instance, so they should have no state.

Clone this wiki locally