Close

AI LangChain4j - Advanced JSON Schema Elements

[Last Updated: Jan 23, 2026]

Beyond basic types like strings and numbers (last tutorial), JSON Schema in LangChain4j supports complex data structures that are essential for real-world applications. This tutorial explores arrays, enums, and nested objects using Ollama's phi3 model running locally.

Key Advanced Schema Elements

LangChain4j provides several schema elements for complex data:

// Arrays and collections
JsonSchemaElement arraySchema = JsonArraySchema.builder()
    .items(itemSchema)
    .build();

// Enumerated values
JsonSchemaElement enumSchema = JsonEnumSchema.builder()
    .enumValues("ACTIVE",
                                                                                  "INACTIVE",
                                                                                  "PENDING")
    .build();

// Nested objects
JsonSchemaElement addressSchema = JsonObjectSchema.builder()
    .addStringProperty("street")
    .addStringProperty("city")
    .build();

Working with Arrays

Arrays are essential when you need to extract multiple items from text. The JsonArraySchema allows you to define arrays of any schema type.

Example: Extracting Multiple Entities

This example demonstrates extracting multiple people from a single text using an array schema. We'll extract a list of people with their names and ages.

package com.logicbig.example;

import com.fasterxml.jackson.annotation.JsonProperty;

public record Person(
    String name,
    int age) {}
package com.logicbig.example;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.request.ChatRequest;
import dev.langchain4j.model.chat.request.ResponseFormat;
import dev.langchain4j.model.chat.request.ResponseFormatType;
import dev.langchain4j.model.chat.request.json.JsonArraySchema;
import dev.langchain4j.model.chat.request.json.JsonObjectSchema;
import dev.langchain4j.model.chat.request.json.JsonSchema;
import dev.langchain4j.model.chat.request.json.JsonSchemaElement;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.ollama.OllamaChatModel;
import java.util.List;

public class PeopleArrayExample {
    public static void main(String[] args) {
        // Create Ollama ChatModel with phi3
        ChatModel chatModel =
                OllamaChatModel.builder()
                               .baseUrl("http://localhost:11434")
                               .modelName("phi3:mini-128k")
                               .logRequests(true)
                               .logResponses(true)
                               .build();

        // Define schema for a single person
        JsonSchemaElement personSchema = JsonObjectSchema.builder()
                                                         .addStringProperty("name")
                                                         .addIntegerProperty("age")
                                                         .required("name", "age")
                                                         .build();

        // Create array schema for multiple people
        JsonSchemaElement peopleArraySchema = JsonArraySchema.builder()
                                                             .items(personSchema)
                                                             .build();

        // Create JSON Schema with array as root
        JsonSchema jsonSchema = JsonSchema.builder()
                                          .name("People")
                                          .rootElement(peopleArraySchema)
                                          .build();

        // Create response format
        ResponseFormat responseFormat = ResponseFormat.builder()
                                                      .type(ResponseFormatType.JSON)
                                                      .jsonSchema(jsonSchema)
                                                      .build();

        // Text containing multiple people
        String text = """
                In the meeting we had John who is 42 years old, 
                Sarah who is 35 years old, and Michael who is 28 years old.
                Also present was Lisa, aged 31.
                """;

        UserMessage userMessage = UserMessage.from(text);

        // Create chat request
        ChatRequest chatRequest = ChatRequest.builder()
                                             .responseFormat(responseFormat)
                                             .messages(userMessage)
                                             .build();

        // Send request and get response
        ChatResponse chatResponse = chatModel.chat(chatRequest);

        // Extract JSON response
        String jsonResponse = chatResponse.aiMessage().text();
        System.out.println("JSON Response: " + jsonResponse);

        // Parse JSON to List of Person objects
        ObjectMapper mapper = new ObjectMapper();
        try {
            List<Person> people = mapper.readValue(jsonResponse,
                                                   new TypeReference<List<Person>>() {});

            System.out.println("\nExtracted People:");
            for (Person person : people) {
                System.out.println(person);
            }
        } catch (Exception e) {
            System.err.println("Error parsing JSON: " + e.getMessage());
        }
    }
}

Output

JSON Response: [{"name": "John", "age": 42}, {"name": "Sarah", "age": 35}, {"name":"Michael","age":28}, {"name":"Lisa","age":31}]


Extracted People:
Person[name=John, age=42]
Person[name=Sarah, age=35]
Person[name=Michael, age=28]
Person[name=Lisa, age=31]

Understanding Array Schema

The key component is JsonArraySchema which defines what type of items the array contains. In our example:

  • JsonArraySchema.builder().items(personSchema) creates an array of person objects
  • Each person object has name (string) and age (integer) properties
  • The LLM identifies multiple people in the text and returns them as a JSON array

Working with Enums

Enums are useful when you have a fixed set of possible values. The JsonEnumSchema ensures the LLM only returns one of the specified values.

Example: Status Extraction with Enums

This example shows how to extract status information using an enum schema. We'll define specific status values and extract them from text.

package com.logicbig.example;

public record ItemStatus(String itemName,
                         Status status) {
}
package com.logicbig.example;

public enum Status {
    ACTIVE,
    INACTIVE,
    PENDING,
    COMPLETED
}
package com.logicbig.example;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.request.ChatRequest;
import dev.langchain4j.model.chat.request.ResponseFormat;
import dev.langchain4j.model.chat.request.ResponseFormatType;
import dev.langchain4j.model.chat.request.json.*;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.ollama.OllamaChatModel;
import java.util.Arrays;
import java.util.List;

public class StatusEnumExample {
    public static void main(String[] args) {
        // Create Ollama ChatModel with phi3
        ChatModel chatModel =
                OllamaChatModel.builder()
                               .baseUrl("http://localhost:11434")
                               .modelName("phi3:mini-128k")
                               .logRequests(true)
                               .logResponses(true)
                               .build();

        // Create enum schema for status
        JsonSchemaElement statusSchema =
                JsonEnumSchema.builder()
                              .enumValues(Arrays.stream(Status.values())
                                                .map(Enum::name)
                                                .toList())
                              .description("Current status of the item")
                              .build();

        // Create object schema with status field
        JsonSchemaElement itemSchema = JsonObjectSchema.builder()
                                                       .addStringProperty("itemName")
                                                       .addProperty("status", statusSchema)
                                                       .required("itemName", "status")
                                                       .build();

        JsonSchemaElement itemsArraySchema = JsonArraySchema.builder()
                                                            .items(itemSchema)
                                                            .description("List of items with their statuses")
                                                            .build();
        // Create JSON Schema
        JsonSchema jsonSchema = JsonSchema.builder()
                                          .name("ItemStatusList")
                                          .rootElement(itemsArraySchema)
                                          .build();

        // Create response format
        ResponseFormat responseFormat = ResponseFormat.builder()
                                                      .type(ResponseFormatType.JSON)
                                                      .jsonSchema(jsonSchema)
                                                      .build();

        // Text describing status
        String text = """
                The 'Website Redesign' is currently active and progressing well.
                The 'Database Migration' has been completed successfully.
                The 'User Authentication' is still pending review.
                """;

        UserMessage userMessage = UserMessage.from(text);

        // Create chat request
        ChatRequest chatRequest = ChatRequest.builder()
                                             .responseFormat(responseFormat)
                                             .messages(userMessage)
                                             .build();

        System.out.println("Extracting status information...");

        // Send request and get response
        ChatResponse chatResponse = chatModel.chat(chatRequest);

        // Extract JSON response
        String jsonResponse = chatResponse.aiMessage().text();
        System.out.println("JSON Response: " + jsonResponse);

        ObjectMapper mapper = new ObjectMapper();
        try {
            List<ItemStatus> items =
                    mapper.readValue(jsonResponse,
                                     new TypeReference<List<ItemStatus>>() {});

            for (ItemStatus item : items) {
                System.out.println(item);
            }

        } catch (Exception e) {
            System.err.println("Error parsing JSON: " + e.getMessage());
        }
    }
}

Output

Extracting status information...
JSON Response: [{"itemName": "Website Redesign", "status": "ACTIVE"}, {"itemName": "Database Migration", "status": "COMPLETED"}, {"itemName":"User Authentication","status": "PENDING"}]
ItemStatus[itemName=Website Redesign, status=ACTIVE]
ItemStatus[itemName=Database Migration, status=COMPLETED]
ItemStatus[itemName=User Authentication, status=PENDING]

Enum Schema Benefits

Using JsonEnumSchema provides several advantages:

  • Type safety: Only predefined values are allowed
  • Consistency: Ensures uniform responses across different inputs
  • Validation: Easy to validate against expected values
  • Documentation: Clearly communicates allowed values to the LLM

Complex Nested Structures

Real-world data often requires nested objects. You can combine different schema types to create complex hierarchical structures.

Example: Company with Employees

This example demonstrates a complex nested structure: a company with multiple employees, where each employee has contact information and a role.

package com.logicbig.example;

import java.util.List;

public record Company(
        String name,
        List<Employee> employees) {}
package com.logicbig.example;

public record Employee(
        String name,
        String email,
        String role) {}
package com.logicbig.example;

import com.fasterxml.jackson.databind.ObjectMapper;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.request.ChatRequest;
import dev.langchain4j.model.chat.request.ResponseFormat;
import dev.langchain4j.model.chat.request.ResponseFormatType;
import dev.langchain4j.model.chat.request.json.*;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.ollama.OllamaChatModel;

public class CompanyExample {
    public static void main(String[] args) {
        // Create Ollama ChatModel with phi3
        ChatModel chatModel =
                OllamaChatModel.builder()
                               .baseUrl("http://localhost:11434")
                               .modelName("phi3:mini-128k")
                               .logRequests(true)
                               .logResponses(true)
                               .build();

        // Create enum schema for roles
        JsonSchemaElement roleSchema = JsonEnumSchema.builder()
                                                     .enumValues("DEVELOPER", "DESIGNER", "MANAGER", "ANALYST")
                                                     .description("Employee role")
                                                     .build();

        // Create schema for employee
        JsonSchemaElement employeeSchema = JsonObjectSchema.builder()
                                                           .addStringProperty("name")
                                                           .addStringProperty("email")
                                                           .addProperty("role", roleSchema)
                                                           .required("name", "email", "role")
                                                           .build();

        // Create array schema for employees
        JsonSchemaElement employeesArraySchema = JsonArraySchema.builder()
                                                                .items(employeeSchema)
                                                                .description("List of company employees")
                                                                .build();

        // Create company schema with employees array
        JsonSchemaElement companySchema = JsonObjectSchema.builder()
                                                          .addStringProperty("name", "Company name")
                                                          .addProperty("employees", employeesArraySchema)
                                                          .required("name", "employees")
                                                          .build();

        // Create JSON Schema
        JsonSchema jsonSchema = JsonSchema.builder()
                                          .name("Company")
                                          .rootElement(companySchema)
                                          .build();

        // Create response format
        ResponseFormat responseFormat = ResponseFormat.builder()
                                                      .type(ResponseFormatType.JSON)
                                                      .jsonSchema(jsonSchema)
                                                      .build();

        // Text describing a company
        String text = """
                Tech Innovations Inc. is a software company with several employees.
                John Smith works as a DEVELOPER with email john@tech.com.
                Sarah Johnson is a DESIGNER with email sarah@tech.com.
                Mike Brown serves as a MANAGER with email mike@tech.com.
                Lisa Wang works as an ANALYST with email lisa@tech.com.
                """;

        UserMessage userMessage = UserMessage.from(text);

        // Create chat request
        ChatRequest chatRequest =
                ChatRequest.builder()
                           .responseFormat(responseFormat)
                           .messages(userMessage)
                           .build();

        System.out.println("Extracting company information...");

        // Send request and get response
        ChatResponse chatResponse = chatModel.chat(chatRequest);

        // Extract JSON response
        String jsonResponse = chatResponse.aiMessage().text();
        System.out.println("JSON Response: " + jsonResponse);

        // Parse JSON to Company object
        ObjectMapper mapper = new ObjectMapper();
        try {
            Company company = mapper.readValue(jsonResponse, Company.class);
            System.out.println(company);
            System.out.println("Number of Employees: " + company.employees().size());
            System.out.println("\nEmployees:");
            for (Employee emp : company.employees()) {
                System.out.println(emp);
            }
        } catch (Exception e) {
            System.err.println("Error parsing JSON: " + e.getMessage());
            System.out.println("\nRaw JSON output demonstrates complex nested structure.");
        }
    }
}

Output

Extracting company information...
JSON Response: {
"name": "Tech Innovations Inc.",
"employees": [
{
"name": "John Smith",
"email": "john@tech.com"
,
"role": "DEVELOPER"
}
,
{
"name": "Sarah Johnson",
"email": "sarah@tech.com",
"role": "DESIGNER"
}]
}

Company[name=Tech Innovations Inc., employees=[Employee[name=John Smith, email=john@tech.com, role=DEVELOPER], Employee[name=Sarah Johnson, email=sarah@tech.com, role=DESIGNER]]]
Number of Employees: 2

Employees:
Employee[name=John Smith, email=john@tech.com, role=DEVELOPER]
Employee[name=Sarah Johnson, email=sarah@tech.com, role=DESIGNER]

Nested Schema Structure

The example shows three levels of nesting:

  1. Company: Top-level object with name and employees array
  2. Employee: Object with name, email, and role
  3. Role: Enum with specific job titles

This demonstrates how LangChain4j can handle complex real-world data structures.

Conclusion

In this tutorial, we've explored advanced JSON Schema elements using Ollama's phi3 model. The output demonstrates that the LLM successfully:

  • Extracted multiple people into a JSON array with correct structure
  • Used enum values properly for status extraction
  • Handled complex nested objects with multiple levels
  • Returned structured data that maps directly to Java objects

These advanced schema elements enable you to extract complex, real-world data structures from unstructured text with type safety and predictability. Using Ollama locally makes this approach cost-effective and private, ideal for development and testing.

Example Project

Dependencies and Technologies Used:

  • langchain4j-ollama 1.10.0 (LangChain4j :: Integration :: Ollama)
  • jackson-databind 2.18.2 (General data-binding functionality for Jackson: works on core streaming API)
  • JDK 17
  • Maven 3.9.11

AI LangChain4j - Advanced JSON Schema Elements with Ollama Select All Download
  • json-schema-advanced
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • PeopleArrayExample.java

    See Also

    Join