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
  }
}

Array parameters

@JsonRPCApi
public class BatchService {

    public int count(String[] items) {
        return items.length;
    }
}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "BatchService#count",
  "params": {
    "items": ["a", "b", "c"]
  }
}

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.