Collections, Maps & Optionals
The extension fully supports Java collection types — List, Set, Map, Optional, and arrays — as both return types and method parameters. Generic type information is preserved at runtime, so Jackson deserializes parameterized types like List<Pojo> or Map<String, Pojo> correctly.
Returning Collections
Methods can return List<T>, Set<T>, or T[]. The result is serialized as a JSON array:
@JsonRPCApi
public class InventoryService {
public List<String> categories() {
return List.of("electronics", "books", "clothing");
}
public List<Product> featured() {
return List.of(
new Product("Laptop", 999.99),
new Product("Novel", 12.99)
);
}
public Set<String> tags() {
return Set.of("sale", "new", "popular");
}
}
Response for InventoryService#categories:
{
"jsonrpc": "2.0",
"id": 1,
"result": ["electronics", "books", "clothing"]
}
Response for InventoryService#featured:
{
"jsonrpc": "2.0",
"id": 2,
"result": [
{ "name": "Laptop", "price": 999.99 },
{ "name": "Novel", "price": 12.99 }
]
}
Returning Maps
Methods can return Map<K, V>. The result is serialized as a JSON object:
@JsonRPCApi
public class ConfigService {
public Map<String, String> settings() {
return Map.of(
"theme", "dark",
"language", "en"
);
}
public Map<String, Product> catalog() {
Map<String, Product> map = new LinkedHashMap<>();
map.put("laptop", new Product("Laptop", 999.99));
map.put("novel", new Product("Novel", 12.99));
return map;
}
}
Response for ConfigService#settings:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"theme": "dark",
"language": "en"
}
}
Response for ConfigService#catalog:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"laptop": { "name": "Laptop", "price": 999.99 },
"novel": { "name": "Novel", "price": 12.99 }
}
}
Returning Optionals
Methods can return Optional<T>. A present value is unwrapped and serialized normally; an empty optional results in null:
@JsonRPCApi
public class LookupService {
public Optional<String> findNickname(String username) {
return nicknameStore.get(username);
}
}
When the value is present:
{
"jsonrpc": "2.0",
"id": 1,
"result": "Johnny"
}
When the value is empty:
{
"jsonrpc": "2.0",
"id": 1,
"result": null
}
Collections as Parameters
Collection types work as method parameters too. The extension preserves generic type information so Jackson deserializes List<Pojo>, Map<String, Pojo>, etc. correctly.
List parameters
@JsonRPCApi
public class BatchService {
public String joinNames(List<String> names) {
return String.join(", ", names);
}
}
Using named parameters:
{
"jsonrpc": "2.0",
"id": 1,
"method": "BatchService#joinNames",
"params": {
"names": ["Alice", "Bob", "Charlie"]
}
}
Using positional parameters:
{
"jsonrpc": "2.0",
"id": 1,
"method": "BatchService#joinNames",
"params": [["Alice", "Bob", "Charlie"]]
}
Map parameters
@JsonRPCApi
public class BatchService {
public String lookup(Map<String, String> data, String key) {
return data.get(key);
}
}
{
"jsonrpc": "2.0",
"id": 1,
"method": "BatchService#lookup",
"params": {
"data": { "host": "localhost", "port": "8080" },
"key": "host"
}
}
Optional parameters
Optional<T> can be used as a method parameter. Pass the value directly (or null for empty):
@JsonRPCApi
public class BatchService {
public String withDefault(String name, Optional<String> title) {
return title.map(t -> t + " " + name).orElse(name);
}
}
With a value present:
{
"jsonrpc": "2.0",
"id": 1,
"method": "BatchService#withDefault",
"params": {
"name": "Alice",
"title": "Dr."
}
}
With an empty optional (pass null):
{
"jsonrpc": "2.0",
"id": 1,
"method": "BatchService#withDefault",
"params": {
"name": "Alice",
"title": null
}
}
Async Variants
All collection and map types work with Uni<T> for async execution:
@JsonRPCApi
public class AsyncInventoryService {
public Uni<List<String>> categories() {
return categoryRepository.listAll();
}
public Uni<Map<String, Product>> catalog() {
return catalogRepository.loadCatalog();
}
}
These follow the same execution semantics described in Execution Modes & Return Types.