CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-immutables--immutables

Comprehensive Java annotation processing framework for generating immutable value objects, marshalers, repositories, and custom code generators with extensive integration support.

Pending
Overview
Eval results
Files

json-marshaling.mddocs/

JSON Marshaling

Built-in JSON marshaling capabilities with customizable field names, subclass handling, and integration with streaming JSON APIs. The JSON marshaling feature generates efficient serialization code that works with the Immutables runtime marshaling framework.

Capabilities

JSON Marshaled Generation

Generate JSON marshaler for immutable types with streaming API support.

/**
 * Generate JSON marshaler for the annotated immutable type.
 * Creates marshalers that integrate with the Immutables JSON framework
 * for efficient streaming serialization and deserialization.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@interface Json.Marshaled {}

Usage Example:

import org.immutables.value.Value;
import org.immutables.value.Json;
import java.util.Optional;
import java.util.List;

@Value.Immutable
@Json.Marshaled
public interface Person {
  String name();
  int age();
  Optional<String> email();
  List<String> tags();
}

// JSON marshaling usage
Person person = ImmutablePerson.builder()
    .name("Alice")
    .age(30)
    .email("alice@example.com")
    .addTags("developer", "java")
    .build();

// Serialize to JSON
String json = PersonMarshaler.instance().toString(person);
// {"name":"Alice","age":30,"email":"alice@example.com","tags":["developer","java"]}

// Deserialize from JSON
Person restored = PersonMarshaler.instance().fromString(json);

Custom Field Names

Customize JSON field names for attributes.

/**
 * Specify custom JSON field name for an attribute.
 * Overrides the default field name derived from the accessor method.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
@interface Json.Named {
  /** Custom JSON field name */
  String value();
}

Usage Example:

import org.immutables.value.Value;
import org.immutables.value.Json;

@Value.Immutable
@Json.Marshaled
public interface ApiResponse {
  @Json.Named("response_code")
  int responseCode();
  
  @Json.Named("data")
  String responseData();
  
  @Json.Named("timestamp_ms")
  long timestampMillis();
}

// Generated JSON uses custom field names
ApiResponse response = ImmutableApiResponse.builder()
    .responseCode(200)
    .responseData("success")
    .timestampMillis(System.currentTimeMillis())
    .build();

String json = ApiResponseMarshaler.instance().toString(response);
// {"response_code":200,"data":"success","timestamp_ms":1640995200000}

Field Exclusion

Exclude specific attributes from JSON serialization.

/**
 * Exclude an attribute from JSON marshaling.
 * The attribute will not appear in serialized JSON output
 * and will not be expected during deserialization.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
@interface Json.Ignore {}

Usage Example:

import org.immutables.value.Value;
import org.immutables.value.Json;
import java.time.Instant;

@Value.Immutable
@Json.Marshaled
public interface UserAccount {
  String username();
  String email();
  
  @Json.Ignore  // Sensitive data not serialized
  String passwordHash();
  
  @Json.Ignore  // Internal field not exposed via JSON
  Instant lastModified();
  
  boolean active();
}

// passwordHash and lastModified are excluded from JSON
UserAccount account = ImmutableUserAccount.builder()
    .username("alice")
    .email("alice@example.com")
    .passwordHash("$2a$10$...")
    .lastModified(Instant.now())
    .active(true)
    .build();

String json = UserAccountMarshaler.instance().toString(account);
// {"username":"alice","email":"alice@example.com","active":true}

Force Empty Values

Force output of empty collections and null optional values.

/**
 * Force output of empty collections or null optional values in JSON.
 * By default, empty collections and absent optionals are omitted
 * from JSON output for compactness.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
@interface Json.ForceEmpty {}

Usage Example:

import org.immutables.value.Value;
import org.immutables.value.Json;
import java.util.List;
import java.util.Optional;
import java.util.Map;

@Value.Immutable
@Json.Marshaled
public interface DataContainer {
  String name();
  
  @Json.ForceEmpty
  List<String> items();      // Always include, even if empty
  
  @Json.ForceEmpty
  Optional<String> description();  // Always include, even if absent
  
  Map<String, Object> metadata();  // Omitted if empty (default behavior)
}

// Empty collections and absent optionals are included when @Json.ForceEmpty
DataContainer container = ImmutableDataContainer.builder()
    .name("test")
    .build();

String json = DataContainerMarshaler.instance().toString(container);
// {"name":"test","items":[],"description":null}
// Note: metadata is omitted since it's empty and not marked @Json.ForceEmpty

Import External Marshalers

Import static marshalers for use in generated marshaling code.

/**
 * Import static classes containing marshalers for use in generated code.
 * Allows reuse of existing marshalers for complex nested types.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@interface Json.Import {
  /** Classes containing static marshalers to import */
  Class<?>[] value();
}

Usage Example:

import org.immutables.value.Value;
import org.immutables.value.Json;
import org.immutables.common.marshaling.Marshaler;
import org.immutables.common.marshaling.Marshaling;
import java.time.LocalDateTime;

// External marshaler class
public class CustomMarshalers {
  public static final Marshaler<LocalDateTime> LOCAL_DATE_TIME = 
      Marshaling.fromString(LocalDateTime::toString, LocalDateTime::parse);
}

@Value.Immutable
@Json.Marshaled
@Json.Import(CustomMarshalers.class)
public interface Event {
  String name();
  LocalDateTime timestamp();  // Uses imported marshaler
  String description();
}

// The generated marshaler uses CustomMarshalers.LOCAL_DATE_TIME
Event event = ImmutableEvent.builder()
    .name("deployment")
    .timestamp(LocalDateTime.now())
    .description("Production deployment")
    .build();

String json = EventMarshaler.instance().toString(event);
// {"name":"deployment","timestamp":"2023-01-01T10:30:00","description":"Production deployment"}

Subclass Marshaling

Handle polymorphic types with expected subclasses.

/**
 * Specify expected subclasses for polymorphic marshaling.
 * Generates marshaling code that can handle multiple implementations
 * of an abstract type or interface.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@interface Json.Subclasses {
  /** Expected subclass types for polymorphic marshaling */
  Class<?>[] value();
}

Usage Example:

import org.immutables.value.Value;
import org.immutables.value.Json;
import java.util.List;
import java.util.Arrays;
import java.util.stream.Collectors;

// Base type
@Value.Immutable
@Json.Marshaled
@Json.Subclasses({Cat.class, Dog.class})
public interface Animal {
  String name();
  String type();
}

// Subclasses
@Value.Immutable
@Json.Marshaled
public interface Cat extends Animal {
  @Value.Default
  default String type() { return "cat"; }
  boolean indoor();
}

@Value.Immutable
@Json.Marshaled
public interface Dog extends Animal {
  @Value.Default
  default String type() { return "dog"; }
  String breed();
}

// Polymorphic marshaling
List<Animal> animals = Arrays.asList(
    ImmutableCat.builder().name("Whiskers").indoor(true).build(),
    ImmutableDog.builder().name("Rex").breed("Golden Retriever").build()
);

// AnimalMarshaler can handle both Cat and Dog instances
String json = animals.stream()
    .map(animal -> AnimalMarshaler.instance().toString(animal))
    .collect(Collectors.joining(",", "[", "]"));

Generated Marshaler Classes

When @Json.Marshaled is applied, the annotation processor generates:

Marshaler Class

  • Naming: {TypeName}Marshaler
  • Singleton: Static instance() method for singleton access
  • Thread-Safe: Generated marshalers are thread-safe

Serialization Methods

// Generated marshaler methods
public final class PersonMarshaler extends Marshaler<Person> {
  public static PersonMarshaler instance() { ... }
  
  // String serialization
  public String toString(Person instance) { ... }
  
  // Writer serialization  
  public void toWriter(Person instance, Writer writer) throws IOException { ... }
  
  // JsonGenerator serialization (Jackson integration)
  public void toGenerator(Person instance, JsonGenerator generator) throws IOException { ... }
}

Deserialization Methods

// Generated unmarshaler methods
public final class PersonMarshaler extends Marshaler<Person> {
  // String deserialization
  public Person fromString(String json) throws IOException { ... }
  
  // Reader deserialization
  public Person fromReader(Reader reader) throws IOException { ... }
  
  // JsonParser deserialization (Jackson integration)
  public Person fromParser(JsonParser parser) throws IOException { ... }
}

Integration with Marshaling Framework

The generated marshalers integrate with the org.immutables.common.marshaling framework:

import org.immutables.common.marshaling.Marshaling;

// Create custom marshalers
Marshaler<LocalDate> dateMarshaler = Marshaling.fromString(
    LocalDate::toString, 
    LocalDate::parse
);

// Combine with generated marshalers
Person person = PersonMarshaler.instance().fromString(jsonString);

Install with Tessl CLI

npx tessl i tessl/maven-org-immutables--immutables

docs

attributes.md

bean-style.md

code-generation.md

collections.md

core-immutable.md

index.md

integrations.md

json-marshaling.md

runtime-marshaling.md

styling.md

tile.json