Domino-jackson is an annotation-processor-based JSON mapper that works on both the client side (GWT and J2CL) and the JVM. It is based on gwt-jackson; thanks to Nicolas Morel for the original work.
The same generated mappers can be used on the server and in the browser, enabling shared DTOs, easier testing, and consistent JSON behavior across environments.
- Overview
- Modules
- Requirements
- Install
- Quick example
- Annotations
- Registry generation
- Custom serializers and deserializers
- Runtime contexts
- Build and test
- Documentation
- License
Domino-jackson generates JSON mappers, readers, and writers at compile time. It avoids runtime reflection (important for GWT/J2CL) and provides a small runtime API that works the same on JVM and browser targets.
Core capabilities include:
- Compile-time generation of
ObjectMapper,ObjectReader, andObjectWriterimplementations. - Optional registry generation for type-safe mapper lookups.
- Custom serializers/deserializers via annotations.
- Consistent behavior across JVM, GWT, and J2CL.
domino-jackson: runtime library, JSON reader/writer APIs, and GWT module resources.domino-jackson-processor: annotation processor that generates mapper implementations and registries.
- Java 11+ for builds (the parent POM sets
maven.compiler.releaseto 11). - Maven 3.6.0+ (matches the project documentation).
- A build that runs annotation processing (Maven or Gradle).
- For GWT/J2CL usage, include the GWT module
org.dominokit.jackson.Jackson(seedomino-jackson/src/main/module.gwt.xml).
Add the runtime dependency and the processor. Replace VERSION with a release or snapshot:
<properties>
<domino.jackson.version>VERSION</domino.jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>org.dominokit</groupId>
<artifactId>domino-jackson</artifactId>
<version>${domino.jackson.version}</version>
</dependency>
<dependency>
<groupId>org.dominokit</groupId>
<artifactId>domino-jackson-processor</artifactId>
<version>${domino.jackson.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>For Maven annotation processing (recommended):
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.dominokit</groupId>
<artifactId>domino-jackson-processor</artifactId>
<version>${domino.jackson.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>If you use GWT, add the module inherit:
<inherits name="org.dominokit.jackson.Jackson"/>Define a POJO and a mapper interface, then use the generated implementation:
public class Person {
public String firstName;
public String lastName;
}
@JSONMapper
public interface PersonMapper extends ObjectMapper<Person> {}
PersonMapper mapper = PersonMapperImpl.INSTANCE;
Person person = mapper.read("{\"firstName\":\"Ada\",\"lastName\":\"Lovelace\"}");
String json = mapper.write(person);Alternatively, annotate the POJO directly:
@JSONMapper
public class Person {
public String firstName;
public String lastName;
}
Person person = new Person();
person.firstName = "Ada";
person.lastName = "Lovelace";
String json = Person_MapperImpl.INSTANCE.write(person);The processor generates PersonMapperImpl (for interface-based mappers) or Person_MapperImpl (for POJO-based mappers), each with a public INSTANCE singleton.
@JSONMapperon a POJO or on an interface that extendsObjectMapper<T>generates a mapper.@JSONReaderon an interface that extendsObjectReader<T>generates a reader-only mapper.@JSONWriteron an interface that extendsObjectWriter<T>generates a writer-only mapper.@JSONRegistration("Prefix")on a package generates a registry namedPrefixJsonRegistry.
Domino-jackson supports the following Jackson annotations (with noted limitations):
@JsonIgnoreProperties(ignoreUnknown = true)@JsonIgnore@JsonProperty@JsonFormat@JsonInclude@JsonDeserialize(builder = SomeBuilder.class)@JsonPOJOBuilder(buildMethodName = "create")@JsonIdentityInfo(limited:@JsonIdentityReferenceis not supported)@JsonTypeInfo(partial support; see Polymorphism)@JsonSubTypes
Domino-jackson supports any composition of the following:
- Primitives and boxed types, plus
String. - Special types:
BigInteger,BigDecimal,Date,java.sql.Date,Time,Timestamp,Void. - Enums.
- 1D and 2D arrays of primitives, boxed types, and special types.
- Collections:
List,Set,Queue,Stack,Vector,SortedSet,Abstract*collections, and common concrete types. - Maps:
Map,SortedMap,EnumMap,HashMap,TreeMap,LinkedHashMap,IdentityHashMap, and other common variants. - Nested POJOs (beans) composed of the above.
Use @JSONRegistration to generate a JsonRegistry that maps TypeToken to mappers/readers/writers:
@JSONRegistration("App")
package com.example.app;
import org.dominokit.jackson.annotation.JSONRegistration;JsonRegistry registry = AppJsonRegistry.getInstance();
ObjectMapper<Person> mapper = registry.getMapper(TypeToken.of(Person.class));For parameterized types in GWT/J2CL, use nested TypeToken values:
TypeToken<List<Map<Integer, Person>>> type =
new TypeToken<List<Map<Integer, Person>>>(
List.class,
new TypeToken<Map<Integer, Person>>(
Map.class,
TypeToken.of(Integer.class),
TypeToken.of(Person.class)) {}) {};The processor can handle generic types when concrete type arguments are available at compile time. A common pattern is:
- Define a generic base class.
- Annotate the concrete subclass with
@JSONMapper.
Annotating the generic base type alone does not provide enough type information for code generation. Avoid mapper definitions that use unbound type variables (e.g., ObjectMapper<T>).
Provide custom implementations by extending JsonSerializer<T> or JsonDeserializer<T> and annotating them:
@CustomSerializer(Employee.class)
public class EmployeeSerializer extends JsonSerializer<Employee> {
@Override
protected void doSerialize(JsonWriter writer, Employee value, JsonSerializationContext ctx,
JsonSerializerParameters params) {
writer.value(value.getId() + "," + value.getName());
}
}@CustomDeserializer(Employee.class)
public class EmployeeDeserializer extends JsonDeserializer<Employee> {
@Override
protected Employee doDeserialize(JsonReader reader, JsonDeserializationContext ctx,
JsonDeserializerParameters params) {
String[] parts = reader.nextString().split(",");
Employee employee = new Employee();
employee.setId(Long.parseLong(parts[0]));
employee.setName(parts[1]);
return employee;
}
}If your custom mappers live in a different module or external JAR, you can register them via CustomMappersLoader and Java service loading:
@AutoService(CustomMappersLoader.class)
public class PersonCustomMappersLoader implements CustomMappersLoader {
@Override
public void register(RegistryWrapper registry) {
registry.registerSerializer(
Person.class.getCanonicalName(), PersonCustomBeanSerializer.class);
registry.registerDeserializer(
Person.class.getCanonicalName(), PersonCustomBeanDeserializer.class);
}
}Fields declared in superclasses are included during serialization/deserialization. The inheritance tree can be as deep as needed as long as concrete types are resolvable by the processor.
Domino-jackson supports polymorphic hierarchies using @JsonTypeInfo and @JsonSubTypes with these limitations:
- Only
@JsonTypeInfo(use = Id.NAME)is supported. - Base and subtypes must not be generic types.
- Unbounded wildcards are not supported.
- Mapper definitions cannot use wildcards or type variables in the mapped type.
- Bounded wildcards are supported inside generic collections when bounds are part of the
@JsonTypeInfo/@JsonSubTypescontract. - Bounded wildcards are not supported for member fields of custom generic classes.
JacksonContextProvider selects an implementation appropriate for the runtime:
ServerJacksonContextfor JVM environments.JsJacksonContextfor browser environments (GWT/J2CL), using nativeJSON.stringify.
You can pass JsonSerializationContext and JsonDeserializationContext to control per-call options.
Typical commands:
mvn testto run the default unit tests.mvn -pl domino-jackson -am testto focus on runtime tests.mvn -pl domino-jackson-processor -am testto focus on annotation processor tests.mvn spotless:checkto verify Java formatting.mvn spotless:applyto rewrite sources using the configured formatter.
There are additional profiles such as java17-tests (adds src/it/java) and java23-processors
for newer JDK-specific processor behavior.
Checkout the Quick start guide and visit our documentation for more details.
Apache License 2.0. See LICENSE.
