Skip to main content

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:

  1. Create an instance method which takes no arguments and returns a RunaboutInput.
  2. 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);
}
}
note

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.
warning

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:

  1. Instance Serializers
  2. Generic Serializer
  3. Default Serializer
  4. Input is empty

To read more about the behavior of the default serializer, visit this page.