Learn how to generate structured outputs using OpenAI's API in a Java Spring Boot application. This guide demonstrates how to define JSON schemas and integrate them with the OpenAI API for reliable and maintainable code.
The OpenAI API offers powerful capabilities for natural language understanding and generation. One of its standout features is the ability to produce structured outputs that conform to a specified JSON schema. In this blog post, we'll explore how to integrate the OpenAI API into a Java Spring Boot application to generate structured data. We'll walk through a practical example of creating a daily schedule based on user input, demonstrating how to harness the API's structured output feature for reliable and predictable responses.
Technologies Used: Java, Spring Boot, OpenAI API, JSON Schema, RestTemplate, Jackson
ℹ️ Info
This tutorial demonstrates how to use OpenAI's structured outputs feature, which ensures your API responses conform to a predefined JSON schema - perfect for building reliable applications.
✅ Key Advantages:
- Reliable Data Formatting: Ensures that API responses adhere to a predefined JSON schema
- Ease of Parsing: Structured outputs are easier to parse and integrate into applications
- Reduced Error Handling: Minimizes the need for extensive validation and error checking
- Flexibility: Allows dynamic generation of structured data based on user input
Before we begin, ensure you have the following:
Create a new Spring Boot project or add the following dependencies to your pom.xml:
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Spring Web (includes RestTemplate) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Jackson Databind for JSON processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
Here's a Spring service that interacts with the OpenAI API to generate a daily schedule based on user input, utilizing structured outputs.
⚠️ Critical Requirement
When working with the OpenAI API's structured outputs, it's crucial to include all required fields in your JSON schema. In particular, thejson_schemaobject within theresponse_formatmust include thenamefield; otherwise, you'll encounter an error like:"message": "Missing required parameter: 'response_format.json_schema.name'."Let's ensure our code includes this field and that our JSON schema is valid.
package com.example.openai;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class OpenAIService {
private static final String OPENAI_API_KEY = "YOUR_OPENAI_API_KEY";
private static final String OPENAI_API_URL = "https://api.openai.com/v1/chat/completions";
private static final String MODEL_NAME = "gpt-4o-mini"; // Ensure the model supports structured outputs
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
public OpenAIService() {
this.restTemplate = new RestTemplate();
this.objectMapper = new ObjectMapper();
}
public String generateDailyPlan(String userPreferences) throws Exception {
// Build the request payload
Map<String, Object> payload = new HashMap<>();
payload.put("model", MODEL_NAME);
// Messages
List<Map<String, String>> messages = new ArrayList<>();
messages.add(Map.of("role", "system", "content",
"You are a helpful assistant that creates a structured daily plan in JSON format."));
messages.add(Map.of("role", "user", "content",
"Create a daily plan for me based on the following preferences:\n" + userPreferences));
payload.put("messages", messages);
// Define the JSON schema for structured output
Map<String, Object> jsonSchema = new HashMap<>();
jsonSchema.put("type", "object");
Map<String, Object> properties = new HashMap<>();
Map<String, Object> scheduleProperty = new HashMap<>();
scheduleProperty.put("type", "array");
Map<String, Object> items = new HashMap<>();
items.put("type", "object");
Map<String, Object> itemProperties = new HashMap<>();
itemProperties.put("time", Map.of("type", "string"));
itemProperties.put("activity", Map.of("type", "string"));
items.put("properties", itemProperties);
items.put("required", List.of("time", "activity"));
items.put("additionalProperties", false);
scheduleProperty.put("items", items);
properties.put("schedule", scheduleProperty);
jsonSchema.put("properties", properties);
jsonSchema.put("required", List.of("schedule"));
jsonSchema.put("additionalProperties", false);
// Add response format with JSON schema
Map<String, Object> responseFormat = new HashMap<>();
responseFormat.put("type", "json_schema");
// Include 'name' and 'strict' in 'json_schema' as required by the API
Map<String, Object> jsonSchemaFormat = new HashMap<>();
jsonSchemaFormat.put("name", "daily_plan_response");
jsonSchemaFormat.put("schema", jsonSchema);
jsonSchemaFormat.put("strict", true);
responseFormat.put("json_schema", jsonSchemaFormat);
payload.put("response_format", responseFormat);
// Set headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(OPENAI_API_KEY);
// Create the request entity
HttpEntity<String> requestEntity = new HttpEntity<>(
objectMapper.writeValueAsString(payload), headers);
// Send the request and receive the response
ResponseEntity<String> responseEntity = restTemplate.exchange(
OPENAI_API_URL, HttpMethod.POST, requestEntity, String.class);
// Parse the assistant's response
JsonNode responseJson = objectMapper.readTree(responseEntity.getBody());
String assistantContent = responseJson
.path("choices")
.get(0)
.path("message")
.path("content")
.asText();
return assistantContent; // This is your structured JSON output
}
}
To expose an endpoint that uses the OpenAIService, you can create a simple REST controller.
package com.example.openai;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class OpenAIController {
private final OpenAIService openAIService;
public OpenAIController(OpenAIService openAIService) {
this.openAIService = openAIService;
}
@PostMapping("/generate-daily-plan")
public String generateDailyPlan(@RequestBody UserPreferencesDto preferencesDto) {
try {
String result = openAIService.generateDailyPlan(preferencesDto.getPreferences());
return result;
} catch (Exception e) {
e.printStackTrace();
return "{\"error\": \"An error occurred while generating the daily plan.\"}";
}
}
}
Create a DTO class to represent the user's preferences:
package com.example.openai;
public class UserPreferencesDto {
private String preferences;
public String getPreferences() {
return preferences;
}
public void setPreferences(String preferences) {
this.preferences = preferences;
}
}
schedule
array, where each item in the array is an object containing time and activity fields.json_schema object within the response_format now includes the required name and strict fields.Send a POST request to http://localhost:8080/api/generate-daily-plan with the following JSON body:
{
"preferences": "I wake up at 6 AM, prefer jogging in the morning, and work from 8 AM to 4 PM."
}
{
"schedule":[
{
"activity":"Wake Up",
"time":"6:00 AM"
},
{
"activity":"Morning Jog",
"time":"6:15 AM"
},
{
"activity":"Shower and Get Ready",
"time":"7:00 AM"
},
{
"activity":"Breakfast",
"time":"7:30 AM"
},
{
"activity":"Commute to Work",
"time":"7:45 AM"
},
{
"activity":"Start Work",
"time":"8:00 AM"
},
{
"activity":"Work",
"time":"8:00 AM - 12:00 PM"
},
{
"activity":"Lunch Break",
"time":"12:00 PM"
},
{
"activity":"Work",
"time":"1:00 PM - 4:00 PM"
},
{
"activity":"Commute Home",
"time":"4:00 PM"
},
{
"activity":"Relax/Free Time",
"time":"4:30 PM"
},
{
"activity":"Dinner",
"time":"6:30 PM"
},
{
"activity":"Evening Activity (Reading/TV/Exercise)",
"time":"7:00 PM"
},
{
"activity":"Prepare for Next Day",
"time":"8:30 PM"
},
{
"activity":"Wind Down/Bedtime Routine",
"time":"9:30 PM"
},
{
"activity":"Sleep",
"time":"10:00 PM"
}
]
}
By integrating OpenAI's API into your Java Spring Boot applications, you can generate structured outputs that conform to a specified JSON schema. This ensures that the data is reliable, easy to parse, and reduces the need for extensive validation. Including all required fields in your JSON schema, such as the name and strict fields within json_schema, is essential for the API to process your request correctly.
Whether you're creating daily plans, generating reports, or transforming unstructured text into structured data, the OpenAI API provides a robust solution for your Java applications.
If you found this article helpful and would like to discuss how these concepts can be applied to your project, I'd love to hear from you.