# How to Use Jackson Annotations for LLM-Friendly Domain Objects in Embabel

> Learn to use Jackson annotations like JsonProperty and JsonClassDescription in Embabel. Create LLM-friendly domain objects and generate accurate JSON schemas for reliable deserialization.

- Repository: [Embabel/embabel-agent](https://github.com/embabel/embabel-agent)
- Tags: how-to-guide
- Published: 2026-08-08

---

**Annotate your Java domain classes with `@JsonClassDescription`, `@JsonCreator`, `@JsonProperty`, and `@JsonPropertyDescription` to enable Embabel to generate precise JSON schemas for LLM prompts and reliably deserialize model outputs back into immutable Java objects.**

The `embabel/embabel-agent` repository provides a framework for integrating large language models into Java applications through structured output parsing. When you use Jackson annotations for LLM-friendly domain objects in Embabel, the framework can automatically generate JSON schemas from your classes, embed descriptive metadata into LLM prompts, and safely reconstruct objects from model-generated JSON.

## Core Jackson Annotations for Embabel Domain Objects

Embabel relies on four primary Jackson annotations to define the structure and documentation of domain classes. These annotations control both the prompt generation phase and the subsequent deserialization of LLM responses.

### @JsonClassDescription

The **`@JsonClassDescription`** annotation supplies human-readable documentation that Embabel injects directly into LLM prompts. This description tells the model what the object represents and how it should be constructed.

In [`src/test/java/com/embabel/example/simple/horoscope/java/Writeup.java`](https://github.com/embabel/embabel-agent/blob/main/src/test/java/com/embabel/example/simple/horoscope/java/Writeup.java), the class-level description clarifies the object's purpose:

```java
@JsonClassDescription("Writeup relating to a person's horoscope and relevant news")
public class Writeup {
    // ...
}

```

### @JsonCreator

The **`@JsonCreator`** annotation marks the constructor or factory method that Jackson (and Embabel) must use for deserialization. When applied to a constructor, it signals that the LLM must output a JSON object whose keys match the constructor parameters.

As shown in [`src/test/java/com/embabel/example/simple/horoscope/java/StarPerson.java`](https://github.com/embabel/embabel-agent/blob/main/src/test/java/com/embabel/example/simple/horoscope/java/StarPerson.java):

```java
@JsonCreator
public StarPerson(
    @JsonProperty("name") String name,
    @JsonPropertyDescription("Star sign") @JsonProperty("sign") String sign) {
    this.name = name;
    this.sign = sign;
}

```

### @JsonProperty

The **`@JsonProperty`** annotation declares the JSON field name for a Java property and optionally marks it as required. Embabel uses this mapping to validate LLM output and generate accurate JSON schemas for the prompt.

### @JsonPropertyDescription

The **`@JsonPropertyDescription`** annotation adds semantic context to individual fields. This description appears in the LLM prompt, helping the model understand the expected content and format of each property.

## How Embabel Processes Jackson Annotations

Embabel scans annotated domain classes through three distinct phases to ensure reliable structured output from LLMs.

**Prompt Generation** – When requesting a domain object from an LLM, Embabel introspects the class for Jackson annotations. It constructs a JSON schema that incorporates field names, required flags from `@JsonProperty`, and descriptions from both `@JsonClassDescription` and `@JsonPropertyDescription`.

**LLM Guidance** – The generated schema is embedded directly into the prompt, instructing the model exactly which fields to emit and what each field represents. This reduces hallucination and format errors in the model response.

**Deserialization** – After receiving JSON from the LLM, Embabel delegates to Jackson. Using the `@JsonCreator` constructor and `@JsonProperty` mappings, Jackson instantiates the object reliably, even when working with immutable final fields.

## Practical Implementation Example

The horoscope test package in `embabel-agent-api` demonstrates the complete pattern for creating LLM-friendly domain objects.

First, define a simple immutable object with class-level description, as seen in [`src/test/java/com/embabel/example/simple/horoscope/java/Writeup.java`](https://github.com/embabel/embabel-agent/blob/main/src/test/java/com/embabel/example/simple/horoscope/java/Writeup.java):

```java
@JsonClassDescription("Writeup relating to a person's horoscope and relevant news")
public class Writeup {

    private final String text;

    @JsonCreator
    public Writeup(@JsonProperty("text") String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}

```

For objects requiring property-level documentation, reference [`src/test/java/com/embabel/example/simple/horoscope/java/StarPerson.java`](https://github.com/embabel/embabel-agent/blob/main/src/test/java/com/embabel/example/simple/horoscope/java/StarPerson.java):

```java
@JsonClassDescription("Person with astrology details")
public class StarPerson {

    private final String name;
    private final String sign;

    @JsonCreator
    public StarPerson(
        @JsonProperty("name") String name,
        @JsonPropertyDescription("Star sign") @JsonProperty("sign") String sign) {
        this.name = name;
        this.sign = sign;
    }

    // Getters omitted for brevity
}

```

Embabel uses these definitions to construct prompts that guide the LLM toward valid JSON outputs. The integration tests in [`LLMOpenAiGuardRailsIntegrationIT.java`](https://github.com/embabel/embabel-agent/blob/main/LLMOpenAiGuardRailsIntegrationIT.java) validate that models respect these Jackson-annotated schemas when operating in structured output mode.

## Best Practices for LLM-Friendly Domain Models

Follow these patterns to maximize reliability when using Jackson annotations with Embabel:

- **Prefer immutable fields** – Declare fields as `final` and use a `@JsonCreator` constructor. This pattern ensures thread safety and works seamlessly with Jackson's deserialization.
- **Document every property** – Apply `@JsonPropertyDescription` to each significant field. Rich descriptions improve the LLM's understanding of semantic requirements and reduce invalid outputs.
- **Mark required fields explicitly** – Use `@JsonProperty(required = true)` on mandatory parameters to enforce presence and prevent deserialization failures from incomplete model responses.
- **Keep descriptions concise** – Class and property descriptions appear directly in LLM prompts. Overly verbose text consumes context window space and may degrade model performance.

## Summary

- **`@JsonClassDescription`** provides high-level context about the domain object's purpose for LLM prompts.
- **`@JsonCreator`** designates the constructor Jackson uses to rebuild objects from JSON, supporting immutable designs.
- **`@JsonProperty`** maps JSON fields to Java properties and validates required fields.
- **`@JsonPropertyDescription`** adds field-level documentation that guides the LLM toward correct value selection.
- The `embabel/embabel-agent` framework uses these annotations to automate schema generation, prompt enrichment, and type-safe deserialization.

## Frequently Asked Questions

### How does Embabel handle immutable domain objects with Jackson?

Embabel leverages the **`@JsonCreator`** annotation to instantiate immutable classes. By annotating a constructor that accepts all required fields as parameters, Jackson can populate final fields without requiring setters. This pattern appears throughout the test fixtures in [`JavaStructuredOutputFixtures.java`](https://github.com/embabel/embabel-agent/blob/main/JavaStructuredOutputFixtures.java), demonstrating reliable deserialization of immutable structures.

### Which Jackson annotation is most important for improving LLM output quality?

While all four annotations contribute to reliability, **`@JsonPropertyDescription`** provides the most direct impact on output quality. By describing the semantic meaning of each field, you guide the LLM to generate contextually appropriate values rather than generic placeholders.

### Can I use standard JavaBeans setters instead of `@JsonCreator`?

Yes, Jackson supports setter-based deserialization, but Embabel strongly recommends the **`@JsonCreator`** pattern with immutable fields. This approach prevents partial construction states and ensures that objects remain valid after deserialization, which is critical when handling potentially malformed JSON from LLM outputs.

### Where can I find integration tests demonstrating structured output with OpenAI models?

The [`LLMOpenAiGuardRailsIntegrationIT.java`](https://github.com/embabel/embabel-agent/blob/main/LLMOpenAiGuardRailsIntegrationIT.java) file in the `embabel-agent-openai-autoconfigure` module contains comprehensive integration tests. These tests demonstrate how Embabel configures OpenAI models to respect Jackson-annotated schemas and validate that the generated JSON conforms to the expected domain object structure.