Custom serializers
There are two ways to implement Runabout serializers. They can be implement on individual classes to go from an instance of that class to Runabout Input, or you can implement a generic catch-all serializer that can handle Objects of different types.
A single java project can contain both instance serializers and a generic serializer. Runabout will attempt to use instance serializers first, and if no serializer can be found for an object, the generic serializer will be invoked.
1. Instance implementations
Instance implementations of the serializer interface allow for a custom behavior for an instance of a class to be "serialized" to a Runabout Input.
To implement an instance serializer:
- Create an instance method which takes no arguments and returns a
RunaboutInput
. - Annotate the method with the
ToRunabout
annotation.
Example
package com.example;
import dev.runabout.RunaboutInput;
import dev.runabout.ToRunabout;
import java.util.Set;
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
@ToRunabout
RunaboutInput toRunabout() {
final String expression = String.format("new Person(\"%s\")", this.name);
final Set<String> dependencies = Set.of(Person.class.getCanonicalName());
return RunaboutInput.of(expression, dependencies);
}
}
Instance serializer methods can have any level of access private
, protected
, package protected
, or public
. Runabout is able to invoke them. Additionally instance serializers methods are inherited from superclasses, which can also be accessed even if private.
2. Generic (catch-all) implementation
Generic implementations of the serializer interface allow for a single static method to convert all types of java objects within the project to Runabout inputs.
To implement a generic serializer:
- Create a class which implements the
RunaboutSerializer
interface by implementing the single method,toRunaboutGeneric
. - Register your class for the SPI via the
META-INF/services
directory so it can be discovered by the library. For more info on how do this, checkout this tutorial.
Your generic serializer should return null
for any inputs that it does not plan on handling. For example if the Object passed in is of type String
your method should return null
, since the default Runabout serializer is able to handle Strings out of the box.
Example
package com.example;
import dev.runabout.RunaboutInput;
import dev.runabout.RunaboutService;
import java.util.Set;
public class GenericSerializer {
private UtilityClass() {
// Static access only.
}
Map.Entry<String,Collection<String>> genericToRunabout(Object object) {
Map.Entry<String,Collection<String>> result = null;
if (object instanceof Person) {
final String expression = String.format("new Person(\"%s\")", this.name);
final Set<String> dependencies = Set.of(Person.class.getCanonicalName());
result = RunaboutInput.of(expression, dependencies);
}
return result;
}
}
Serializers for complex objects
Oftentimes the objects we need to serialize are more complex than a simple class with a single String field.
Order of operations
Runabout can support projects that contain both instance implementations as well as a generic implementation of the serializer interface. The order in which they are tried is like so:
- Instance Serializers
- Generic Serializer
- Default Serializer
- Input is empty
To read more about the behavior of the default serializer, visit this page.