Structured Output Converter in Spring AI
Structured Output Converter in Spring AI
Suppose you ask OpenAI:
"Provide the major cities of India."
Normally, AI returns plain text.
India has many major cities such as Mumbai, Delhi, Bengaluru, Hyderabad and Chennai.In Java, you'll get:
String response = chatClient.prompt()
.user("Provide major cities of India")
.call()
.content();Type:
StringProblem:
- It's just text.
- You have to parse it yourself.
- AI may change the format every time.
Step 2: Structured Output
Instead of asking AI for plain text, we ask:
"Return the response in a structured format."
{
"country": "India",
"cities": [
"Mumbai",
"Delhi",
"Pune"
]
}
This is called Structured Output.
It means:
AI returns JSON instead of plain text.
Step 3: POJO (Plain Old Java Object)
Now create a Java class (or record) to hold this JSON.
public record CountryCities(
String country,
List<String> cities
) {}This is your POJO/DTO.
It represents:
{ "country":"India", "cities":[...] }
Step 4: StructuredOutputConverter
Spring AI has an interface:StructuredOutputConverter<T>
Purpose:
Convert AI response into a Java object
Think of it like a translator.
AI JSON │ ▼ StructuredOutputConverter │ ▼ Java Object
Methods
public interface StructuredOutputConverter<T> {
T convert(String text);
String getFormat();
}convert()
Converts
{ "country":"India" }into
CountryCities
getFormat()
Creates instructions for AI.
Example:
Return the response in JSON. { "country":"string", "cities":[] }
Spring AI automatically sends these instructions to OpenAI.
Step 5: BeanOutputConverter
Used when you have a POJO.
Example:
public record CountryCities( String country, List<String> cities ){}
Create converter
BeanOutputConverter<CountryCities> converter = new BeanOutputConverter<>(CountryCities.class);
Flow
Prompt │ ▼ BeanOutputConverter │ ├── getFormat() ▼ OpenAI ▼ JSON ▼ convert() ▼ CountryCities
Example
BeanOutputConverter<CountryCities> converter =
new BeanOutputConverter<>(CountryCities.class);
String prompt = """
Provide major cities of India.
%s
""".formatted(converter.getFormat());
String response = chatClient.prompt()
.user(prompt)
.call()
.content();
CountryCities obj = converter.convert(response);BeanOutputConverter<CountryCities> converter =new BeanOutputConverter<>(CountryCities.class);
String prompt = """
Provide major cities of India.
%s
""".formatted(converter.getFormat());
String response = chatClient.prompt()
.user(prompt)
.call()
.content();
CountryCities obj = converter.convert(response);
Step 6:
.entity()(Modern Spring AI)
Instead of writing:
BeanOutputConverter<CountryCities> converter =
new BeanOutputConverter<>(CountryCities.class);
Spring AI lets you write
CountryCities obj = chatClient
.prompt()
.user("Major cities of India")
.call()
.entity(CountryCities.class);
Internally it does:
BeanOutputConverter + convert() + getFormat()
for you.
That's why. entity
()is the recommended approach.
Step 7: ListOutputConverter
Suppose you only need city names.
AI response:
[ "Mumbai", "Delhi", "Pune" ]
Java type
List<String>Use
List<String> cities = chatClient
.prompt()
.user("List major cities of India")
.call()
.entity(new ListOutputConverter());
Output
[ Mumbai, Delhi, Pune ]
Flow
Prompt │ ▼ ListOutputConverter │ ▼ JSON Array │ ▼ List<String>
Step 8: MapOutputConverter
Suppose every response has different fields.
AI returns
{ "country":"India", "capital":"New Delhi", "currency":"Indian Rupee" }
Instead of creating a POJO
Use
Map<String,Object>
Controller
Map<String,Object> response = chatClient
.prompt()
.user("Give details of India")
.call()
.entity(new MapOutputConverter());Result
response.get("country"); response.get("capital"); response.get("currency");
Flow
Prompt │ ▼ MapOutputConverter │ ▼ JSON Object │ ▼ Map<String,Object>
Step 9: Bean List
Suppose AI returns
[ { "country":"India", "cities":[...] }, { "country":"USA", "cities":[...] } ]
Need
List<CountryCities>
Use
List<CountryCities> list =
chatClient.prompt()
.call()
.entity(new ParameterizedTypeReference<List<CountryCities>>() {});
Complete Picture
StructuredOutputConverter<T> ▲ ┌─────────────────┼─────────────────┐ │ │ │ │ │ │ BeanOutputConverter ListOutputConverter MapOutputConverter │ │ │ ▼ ▼ ▼ CountryCities List<String> Map<String,Object>
Which one should I use?
Requirement Converter Return Type One custom object BeanOutputConverteror.entity(MyClass.class)CountryCitiesList of strings ListOutputConverterList<String>Dynamic key-value data MapOutputConverterMap<String, Object>List of custom objects ParameterizedTypeReference<List<MyClass>>List<MyClass>
Interview Summary (Easy to Remember)
- POJO/DTO → Java class that stores the AI response.
- Structured Output → AI returns JSON instead of plain text.
- StructuredOutputConverter → Interface that converts AI JSON into Java objects.
- BeanOutputConverter → Converts JSON to a custom POJO like
CountryCities.- ListOutputConverter → Converts JSON array to
List<String>.- MapOutputConverter → Converts JSON object to
Map<String, Object>.- .entity() → Modern Spring AI shortcut that automatically uses the appropriate structured output conversion, so you usually don't need to create a
BeanOutputConvertermanually.
A simple way to remember them is:
- Bean = One Java object
- List = Many strings
- Map = Dynamic key-value pairs
- StructuredOutputConverter = Parent interface that all these converters implement
package com.example.openai.controller;
import com.example.openai.model.CountryCities;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.converter.ListOutputConverter;
import org.springframework.ai.converter.MapOutputConverter;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class StructuredOutputController {
private final ChatClient chatClient;
public StructuredOutputController(ChatClient.Builder builder) {
this.chatClient = builder
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
}
/*
1. Bean Output Converter
AI Response:
{
country:"India",
cities:["Mumbai","Delhi"]
}
Converted into:
CountryCities object
*/
@GetMapping("/chat-bean")
public ResponseEntity<CountryCities> chatBean(
@RequestParam String country) {
CountryCities response = chatClient
.prompt()
.user("""
Provide major cities of %s.
Return country name and cities.
""".formatted(country))
.call()
.entity(CountryCities.class);
return ResponseEntity.ok(response);
}
/*
2. List Output Converter
AI Response:
[
"Mumbai",
"Delhi",
"Pune"
]
Converted into:
List<String>
*/
@GetMapping("/chat-list")
public ResponseEntity<List<String>> chatList(
@RequestParam String country) {
List<String> response = chatClient
.prompt()
.user("""
Provide major cities of %s.
Return only city names.
""".formatted(country))
.call()
.entity(new ListOutputConverter());
return ResponseEntity.ok(response);
}
/*
3. Map Output Converter
AI Response:
{
country:"India",
capital:"Delhi",
currency:"INR"
}
Converted into:
Map<String,Object>
*/
@GetMapping("/chat-map")
public ResponseEntity<Map<String,Object>> chatMap(
@RequestParam String country) {
Map<String,Object> response = chatClient
.prompt()
.user("""
Provide details about %s.
Include capital,
currency,
population.
""".formatted(country))
.call()
.entity(new MapOutputConverter());
return ResponseEntity.ok(response);
}
/*
4. List of POJO
AI Response:
[
{
country:"India",
cities:["Mumbai","Delhi"]
},
{
country:"USA",
cities:["New York","Chicago"]
}
]
Converted into:
List<CountryCities>
*/
@GetMapping("/chat-bean-list")
public ResponseEntity<List<CountryCities>> chatBeanList(
@RequestParam String message) {
List<CountryCities> response = chatClient
.prompt()
.user(message)
.call()
.entity(
new ParameterizedTypeReference<List<CountryCities>>() {}
);
return ResponseEntity.ok(response);
}
}package com.example.openai.model;
import java.util.List;
public record CountryCities(String country, List<String> cities) {
}Testing -http://localhost:8080/api/chat-bean?country=India{ "country": "India", "cities": [ "Mumbai", "Delhi", "Bengaluru", "Hyderabad", "Pune" ] }http://localhost:8080/api/chat-list?country=India[ "Mumbai", "Delhi", "Pune", "Chennai" ]http://localhost:8080/api/chat-map?country=India{ "country": "India", "capital": "New Delhi", "currency": "Indian Rupee", "population": "1.4 Billion" }http://localhost:8080/api/chat-bean-list?message=Provide details of India and USA with major cities[ { "country": "India", "cities": [ "Mumbai", "Delhi", "Pune" ] }, { "country": "USA", "cities": [ "New York", "Chicago" ] } ]
Comments
Post a Comment