This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /guides/java/extraction/generate-extraction-schema.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. Generating extraction schemas | Nutrient Java SDK

Structured extraction starts with a JSON Schema: the schema tells the extraction model exactly which fields to fill, and an LLM’s structured-output mode guarantees the result matches it. Writing that schema by hand is the slow part — you study sample documents, name every field, decide what’s nullable, what repeats, what’s an enum. Schema generation drafts it for you: describe the document type, state your requirements in plain language, attach a few example documents, and get back a schema ready for structured-output extraction.

This sample shows how to generate an extraction schema from example documents using Nutrient Java SDK. Generation uses a vision model — by default a local server, so documents don’t have to leave your machine.

Download sample

How Nutrient helps

Nutrient Java SDK runs the full schema generation workflow behind a single method call. The SDK handles:

  • Rendering the example documents’ pages and sending them to the vision model in one request
  • Drafting a schema grounded in the document type, your requirement, and the examples
  • Validating and hard-clamping the draft — field budget, nesting depth, and the feature set of your target structured-output dialect
  • Optionally drafting cross-field constraint rules (sums, orderings) and validating each one against the schema
  • Serializing the result to JSON

The result always satisfies the limits you configured, regardless of what the model drafted — the same call with the same settings yields a schema your extraction target accepts.

How generation works

Three inputs shape the schema:

  • Document type (required) — The class of documents the schema must represent, such as “invoice” or “application form”. It grounds the schema’s vocabulary.
  • Requirement (optional) — A free-form description of what the schema must capture: fields of interest, granularity, naming preferences — anything you’d tell a colleague.
  • Example documents (optional, up to five) — Opened documents whose pages are rendered and shown to the model as representative samples. The schema is designed so other documents of the same type also fit, not just the examples. At most 20 pages are sent in total across all examples; pages beyond that bound aren’t rendered.

Generation requires a vision model. By default the SDK talks to a local OpenAI-compatible server at http://localhost:1234/v1 — configure the connection through CustomVlmApiSettings the same way as in the describe image with local AI guide. Schema generation requires the vision schema generation feature in your license.

Targeting a structured-output dialect

LLM providers each accept a different subset of JSON Schema in their structured-output modes — one rejects oneOf, another rejects maxItems, a third silently ignores keywords it doesn’t know. The Compatibility setting picks the target dialect, and the SDK guarantees the generated schema stays inside it:

  • Portable (default) — The intersection accepted by the major structured-output modes and local grammar-constrained inference engines. The safest choice when you don’t know where the schema will run.
  • OpenAI, Anthropic, Gemini — Unlock the keywords that specific provider documents as supported, while enforcing its documented rejections.
  • None — No dialect filter; any standard JSON Schema feature the other knobs enable.

Every generated schema also satisfies the strict-mode invariants shared by all providers: an object root, every property listed in required, additionalProperties: false on every object, and optionality expressed as a null type union rather than a missing requirement.

Bounding the schema

Generated schemas stay within the budgets you set — properties beyond MaxFields are truncated and structures deeper than MaxNestingDepth are flattened, so a verbose model can’t produce an unusable schema. Feature knobs (AllowEnums, AllowNullable, AllowStringFormats, and others) control which JSON Schema features the draft may use.

Cross-field constraints

A schema describes each field in isolation; it can’t say “the subtotal equals the sum of the line item amounts.” Enabling IncludeConstraints asks the model to also encode such relationships as JsonLogic(opens in a new tab) constraint rules — inert JSON expressions your validation layer can evaluate after extraction. Every rule is checked against the generated schema: a rule referencing a field that doesn’t exist is dropped, so the constraints always match the schema they ship with.

Complete implementation

Specify a package name and create a new class:

package io.nutrient.Sample;

Import the classes used in the sample:

import io.nutrient.sdk.Document;
import io.nutrient.sdk.Vision;
import io.nutrient.sdk.requests.SchemaGenerationRequest;
import io.nutrient.sdk.settings.DocumentSettings;
import io.nutrient.sdk.exceptions.NutrientException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

Building the request

Open the example documents in a try-with-resources block so resources are cleaned up after processing, then describe what the schema must represent:

public class GenerateExtractionSchema {
public static void main(String[] args) {
try (Document firstExample = Document.open("input_forms.pdf");
Document secondExample = Document.open("input_forms_detection.pdf")) {
SchemaGenerationRequest request = new SchemaGenerationRequest();
request.setDocumentType("application form");
request.setRequirement(
"Capture the applicant's identity (full name, date of birth, contact details), " +
"every checkbox group as an enumerated field, and the signature and date block. " +
"Repeating rows belong in arrays of objects.");
request.addExampleDocument(firstExample);
request.addExampleDocument(secondExample);

Configuring the schema shape

Create standalone document settings carrying the schema knobs — bound the field budget and opt in to cross-field constraints:

DocumentSettings settings = new DocumentSettings();
var schemaGeneration = settings.getSchemaGenerationSettings();
schemaGeneration.setMaxFields(40);
schemaGeneration.setIncludeConstraints(true);

Generating the schema

Call the static Vision.generateSchema() with the request and settings:

String resultJson = Vision.generateSchema(request, settings);
Files.writeString(Path.of("output.json"), resultJson);
} catch (NutrientException | IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
}

Understanding the output

generateSchema() returns a JSON envelope with two members — the shape is the same whether or not constraints are enabled:

  • schema — The generated JSON Schema, ready to pass to an LLM structured-output mode. Every property carries a description telling the extractor what to put there.
  • constraints — Cross-field rules (empty unless IncludeConstraints is on). Each carries a short id, a one-sentence description, and a rule — a plain, standard JsonLogic expression that evaluates to true when the extracted document is internally consistent, for example, {"==": [{"var": "total"}, {"+": [{"var": "subtotal"}, {"var": "tax_amount"}]}]}.

Pass schema to your extraction call, and after extraction, evaluate each constraint rule against the extracted data with any JsonLogic implementation — a false result flags the document for review. Comparison policy (such as tolerating sub-cent rounding differences in numeric checks) is yours to apply in that evaluation step.

Error handling

Vision API raises VisionException (a NutrientException) when generation fails. Common failure scenarios include a missing document type, more than five example documents, an unreachable vision model endpoint, or a model response that isn’t valid JSON after a retry. In production code, catch NutrientException, return a clear error message, and log failure details for debugging.

Conclusion

The workflow for generating an extraction schema is:

  1. Open up to five representative example documents using try-with-resources for automatic resource cleanup.
  2. Build a SchemaGenerationRequest with the document type (required), a natural-language requirement, and the examples.
  3. Configure the schema shape — field budget, nesting, target dialect, constraints — through the schema generation settings.
  4. Call Vision.generateSchema() to draft, validate, and clamp the schema.
  5. Write the envelope to a file; feed schema to your structured-output extraction and constraints to your validation layer.
  6. Handle NutrientException for robust error recovery.

For configuring the vision model connection, refer to the describe image with local AI guide. For related extraction workflows, refer to the Java SDK guides.

Download this ready-to-use sample package to explore schema generation.