Creating Custom (De)serializers
Suppose you have an object that's not straightforward like a POJO, and you want to serialize it in a special way.
In this example, Class A may contain information about either an array or an instance of Class B. We only want to serialize one of them at a time. The other will always be null.
Attempting to serialize this as a POJO would result in a larger structure containing a null value, a filled value representing the correct structure, and the boolean that tells us which structure to pick.
But we obviously only want it to only result in the correct structure.
Attempting to deserialize either type would result in an error in both cases, because the structure of Class A matches neither an array nor Class B.
To get the correct results, we have to implement a custom deserializer:
import blue.endless.jankson.Jankson;
import blue.endless.jankson.annotation.Deserializer;
import blue.endless.jankson.annotation.Serializer;
import blue.endless.jankson.api.SyntaxError;
public class ClassA {
    private static final Jankson JANKSON = Jankson.builder().build();
    // Variables described above
    // Use the Serializer annotation to create a serializer
    @Serializer
    public JsonElement toJson() {
        // The boolean isClassB is a stand-in for your logic that decides what type to parse
        // You might use an enum if you have more than two types of data
        if (isClassB && classB != null) return JANKSON.toJson(classB);
        // If we can't do that, it might be the other type.
        else if (!isClassB && arr != null) return JANKSON.toJson(arr);
        // This object is invalid. Return an empty JSON object.
        else return JANKSON.toJson(new Object());
    }
    // Use the Deserializer annotation to create a deserializer
    @Deserializer
    // Take a JsonArray as an argument when you want to parse an array
    public static ClassA fromArray(JsonArray array) throws SyntaxError {
        var value = new ClassA();
        // Convert from a JsonArray to an int array
        value.arr = JANKSON.fromJson(array.toJson(), int[].class);
        // Set the boolean so we can re-serialize the data later
        value.isClassB = false;
        return value;
    }
    @Deserializer
    // Take a JsonObject when you want to parse a POJO
    public static ClassA fromObject(JsonObject object) throws SyntaxError {
        var value = new ClassA();
        // Convert from an object to your POJO instance
        value.classB = JANKSON.fromJson(object.toJson(), ClassB.class);
        // Set the boolean so we can re-serialize the data later
        value.isClassB = true;
        return value;
    }
}
import blue.endless.jankson.Jankson
import blue.endless.jankson.annotation.Deserializer
import blue.endless.jankson.annotation.Serializer
import blue.endless.jankson.api.SyntaxError as JanksonSyntaxError
class ClassA {
    // Variables described above
    // Use the Serializer annotation to create a serializer
    @Serializer
    fun toJson(): JsonElement {
        // The boolean isClassB is a stand-in for your logic that decides what type to parse
        // You might use an enum if you have more than two types of data
        return if (isClassB && classB != null) JANKSON.toJson(classB)
        // If we can't do that, it might be the other type.
        else if (!isClassB && arr != null) JANKSON.toJson(arr)
        // This object is invalid. Return an empty JSON object.
        else JANKSON.toJson(Any())
    }
    companion object {
        private val JANKSON: Jankson = Jankson.builder().build()
        // Use the Deserializer annotation to create a deserializer
        @Deserializer // Take a JsonArray as an argument when you want to parse an array
        @Throws(JanksonSyntaxError::class)
        fun fromArray(array: JsonArray): ClassA {
            val value = ClassA()
            // Convert from a JsonArray to an int array
            value.arr = JANKSON.fromJson(array.toJson(), IntArray::class.java)
            // Set the boolean so we can re-serialize the data later
            value.isClassB = false
            return value
        }
        @Deserializer // Take a JsonObject when you want to parse a POJO
        @Throws(JanksonSyntaxError::class)
        fun fromObject(`object`: JsonObject): ClassA {
            val value = ClassA()
            // Convert from an object to your POJO instance
            value.classB = JANKSON.fromJson(`object`.toJson(), ClassB::class.java)
            // Set the boolean so we can re-serialize the data later
            value.isClassB = true
            return value
        }
    }
}
import blue.endless.jankson.Jankson
import blue.endless.jankson.annotation.Deserializer
import blue.endless.jankson.annotation.Serializer
import blue.endless.jankson.api.SyntaxError
class ClassA {
    def JANKSON = Jankson.builder().build()
    // Variables described above
    // Use the Serializer annotation to create a serializer
    @Serializer
    JsonElement toJson() {
        // The boolean isClassB is a stand-in for your logic that decides what type to parse
        // You might use an enum if you have more than two types of data
        if (isClassB && classB != null) return JANKSON.toJson(classB)
        // If we can't do that, it might be the other type.
        else if (!isClassB && arr != null) return JANKSON.toJson(arr)
        // This object is invalid. Return an empty JSON object.
        else return JANKSON.toJson(new Object())
    }
    // Use the Deserializer annotation to create a deserializer
    @Deserializer
    // Take a JsonArray as an argument when you want to parse an array
    static ClassA fromArray(JsonArray array) throws SyntaxError {
        def value = new ClassA()
        // Convert from a JsonArray to an int array
        value.arr = JANKSON.fromJson(array.toJson(), int[].class)
        // Set the boolean so we can re-serialize the data later
        value.isClassB = false
        return value
    }
    @Deserializer
    // Take a JsonObject when you want to parse a POJO
    static ClassA fromObject(JsonObject object) throws SyntaxError {
        def value = new ClassA()
        // Convert from an object to your POJO instance
        value.classB = JANKSON.fromJson(object.toJson(), ClassB.class)
        // Set the boolean so we can re-serialize the data later
        value.isClassB = true
        return value
    }
}