or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-setup.mderrors.mdindex.mdmessages.mdplatform-adapters.mdstreaming.mdstructured-outputs.mdtools.md

structured-outputs.mddocs/

0

# Structured Outputs

1

2

Anthropic Structured Outputs (beta) is a feature that ensures the model will always generate responses that adhere to a supplied JSON schema. The SDK provides automatic JSON schema derivation from Java classes, enabling type-safe structured responses without manual schema definition.

3

4

## Overview

5

6

Structured Outputs guarantees that Claude's responses conform to your specified JSON schema, making it ideal for extracting structured data, generating consistent formats, and building reliable integrations. The SDK automatically converts Java classes into JSON schemas and deserializes responses back into Java objects.

7

8

Key features:

9

- Automatic JSON schema generation from Java class structure

10

- Type-safe response handling with compile-time checking

11

- Support for nested classes, collections, and optional fields

12

- Jackson and Swagger annotations for schema customization

13

- Streaming support with JSON accumulation

14

- Local schema validation before API calls

15

16

## Output Format Configuration

17

18

Configure structured outputs by calling `outputFormat(Class<T>)` on the message builder. This method derives a JSON schema from the provided class and returns a generic builder for type-safe parameter construction.

19

20

```java { .api }

21

public <T> StructuredMessageCreateParams.Builder<T> outputFormat(Class<T> outputClass);

22

public <T> StructuredMessageCreateParams.Builder<T> outputFormat(

23

Class<T> outputClass,

24

JsonSchemaLocalValidation validation

25

);

26

```

27

28

**Parameters:**

29

- `outputClass` - The Java class to derive the JSON schema from

30

- `validation` - Whether to validate the schema locally (default: `JsonSchemaLocalValidation.YES`)

31

32

**Returns:** A `StructuredMessageCreateParams.Builder<T>` for building type-safe parameters

33

34

## StructuredMessageCreateParams

35

36

Generic builder type for creating message requests with structured outputs. When you call `outputFormat(Class<T>)` on a standard builder, it returns this specialized builder parameterized with your output type.

37

38

```java { .api }

39

public final class StructuredMessageCreateParams<T> {

40

public static <T> Builder<T> builder();

41

42

public static final class Builder<T> {

43

public Builder<T> model(Model model);

44

public Builder<T> maxTokens(long maxTokens);

45

public Builder<T> messages(List<MessageParam> messages);

46

public Builder<T> addMessage(MessageParam message);

47

public Builder<T> addUserMessage(String content);

48

public Builder<T> addAssistantMessage(String content);

49

public Builder<T> temperature(Double temperature);

50

public Builder<T> system(String system);

51

public Builder<T> tools(List<ToolUnion> tools);

52

public Builder<T> addTool(Tool tool);

53

public Builder<T> toolChoice(ToolChoice toolChoice);

54

public Builder<T> metadata(Metadata metadata);

55

public Builder<T> stopSequences(List<String> stopSequences);

56

public Builder<T> topK(Long topK);

57

public Builder<T> topP(Double topP);

58

public StructuredMessageCreateParams<T> build();

59

}

60

}

61

```

62

63

## Defining Java Classes

64

65

Classes used for structured outputs must follow specific rules to ensure valid JSON schema generation:

66

67

### Field Declaration Rules

68

69

**Public fields** are included in the JSON schema by default:

70

```java

71

class Person {

72

public String name; // Included

73

public int birthYear; // Included

74

private String internal; // Excluded by default

75

}

76

```

77

78

**Private fields with public getters** are included using the getter method name:

79

```java

80

class Person {

81

private String name;

82

83

public String getName() { // Creates "name" property

84

return name;

85

}

86

}

87

```

88

89

**Non-conventional getter names** require `@JsonProperty` annotation:

90

```java

91

class Person {

92

private String name;

93

94

@JsonProperty

95

public String fullName() { // Creates "fullName" property

96

return name;

97

}

98

}

99

```

100

101

### Nested Classes and Collections

102

103

Classes can contain fields of other class types and use standard Java collections:

104

105

```java

106

class Person {

107

public String name;

108

public int birthYear;

109

}

110

111

class Book {

112

public String title;

113

public Person author; // Nested class

114

public int publicationYear;

115

}

116

117

class BookList {

118

public List<Book> books; // Collection of nested classes

119

}

120

```

121

122

Supported collection types:

123

- `List<T>`

124

- `Set<T>`

125

- `Collection<T>`

126

- Arrays (`T[]`)

127

128

### Required Properties

129

130

Each class must define at least one property. A validation error occurs if:

131

- The class has no fields or getter methods

132

- All public fields/getters are annotated with `@JsonIgnore`

133

- All non-public fields/getters lack `@JsonProperty` annotations

134

- A field uses `Map` type (maps have no named properties)

135

136

## Optional Fields

137

138

Use `java.util.Optional<T>` to represent optional properties. The AI model decides whether to provide a value or leave it empty.

139

140

```java

141

import java.util.Optional;

142

143

class Book {

144

public String title; // Required

145

public Person author; // Required

146

public int publicationYear; // Required

147

public Optional<String> isbn; // Optional

148

public Optional<String> subtitle; // Optional

149

}

150

```

151

152

**Usage:**

153

```java

154

StructuredMessage<Book> response = client.beta().messages().create(params);

155

Book book = response.content().get(0).text().text();

156

157

// Check optional fields

158

if (book.isbn.isPresent()) {

159

System.out.println("ISBN: " + book.isbn.get());

160

} else {

161

System.out.println("No ISBN provided");

162

}

163

```

164

165

## JSON Schema Derivation

166

167

The SDK automatically generates JSON schemas from Java class structure:

168

169

**Input class:**

170

```java

171

class Book {

172

public String title;

173

public Person author;

174

public int publicationYear;

175

public Optional<String> isbn;

176

}

177

178

class Person {

179

public String name;

180

public int birthYear;

181

}

182

```

183

184

**Generated schema (conceptual):**

185

```json

186

{

187

"type": "object",

188

"properties": {

189

"title": { "type": "string" },

190

"author": {

191

"type": "object",

192

"properties": {

193

"name": { "type": "string" },

194

"birthYear": { "type": "integer" }

195

},

196

"required": ["name", "birthYear"]

197

},

198

"publicationYear": { "type": "integer" },

199

"isbn": { "type": "string" }

200

},

201

"required": ["title", "author", "publicationYear"]

202

}

203

```

204

205

Schema generation rules:

206

- Public fields and getter methods become schema properties

207

- Field names become property names (using getter naming conventions)

208

- Java types map to JSON schema types (String → string, int/long → integer, etc.)

209

- `Optional<T>` fields are not included in required array

210

- Nested classes become nested object schemas

211

- Collections become array schemas with item types

212

213

## BetaJsonOutputFormat

214

215

Low-level class for manually defining JSON schemas when automatic derivation is not suitable. This provides complete control over schema structure.

216

217

```java { .api }

218

public final class BetaJsonOutputFormat {

219

public static Builder builder();

220

221

public static final class Builder {

222

public Builder type(Type type);

223

public Builder schema(JsonSchema schema);

224

public Builder build();

225

}

226

227

public enum Type {

228

JSON_SCHEMA

229

}

230

231

public static final class JsonSchema {

232

public static Builder builder();

233

234

public static final class Builder {

235

public Builder type(String type);

236

public Builder properties(Map<String, Object> properties);

237

public Builder required(List<String> required);

238

public Builder additionalProperties(boolean allowed);

239

public JsonSchema build();

240

}

241

}

242

}

243

```

244

245

**Manual schema example:**

246

```java

247

BetaJsonOutputFormat format = BetaJsonOutputFormat.builder()

248

.type(BetaJsonOutputFormat.Type.JSON_SCHEMA)

249

.schema(BetaJsonOutputFormat.JsonSchema.builder()

250

.type("object")

251

.properties(Map.of(

252

"title", Map.of("type", "string"),

253

"year", Map.of("type", "integer")

254

))

255

.required(List.of("title", "year"))

256

.build())

257

.build();

258

```

259

260

## StructuredMessage

261

262

Response type for structured output requests. Extends the standard `Message` class with typed content access methods.

263

264

```java { .api }

265

public final class StructuredMessage<T> extends Message {

266

@Override

267

public List<ContentBlock> content();

268

269

// Type-safe content access

270

public T getStructuredContent();

271

public Optional<T> getStructuredContentSafe();

272

}

273

```

274

275

**Usage:**

276

```java

277

StructuredMessageCreateParams<BookList> params = MessageCreateParams.builder()

278

.model(Model.CLAUDE_SONNET_4_5_20250929)

279

.maxTokens(2048)

280

.outputFormat(BookList.class)

281

.addUserMessage("List famous novels.")

282

.build();

283

284

StructuredMessage<BookList> response = client.beta().messages().create(params);

285

286

// Access structured content

287

response.content().stream()

288

.flatMap(block -> block.text().stream())

289

.flatMap(textBlock -> textBlock.text().books.stream())

290

.forEach(book -> System.out.println(book.title + " by " + book.author.name));

291

```

292

293

## Local Schema Validation

294

295

The SDK performs local validation before sending requests to ensure schemas comply with Anthropic's restrictions. This catches errors early and provides detailed feedback.

296

297

```java { .api }

298

public enum JsonSchemaLocalValidation {

299

YES,

300

NO

301

}

302

```

303

304

### Validation Behavior

305

306

**Local Validation (default):**

307

- Validates schema against Anthropic's subset of JSON Schema specification

308

- Checks for unsupported data types and constructs

309

- Throws exception with detailed error message if validation fails

310

- No API request sent if validation fails

311

312

**Remote Validation:**

313

- Performed by Anthropic API after receiving request

314

- May differ from local validation if SDK version is outdated

315

316

### When to Disable Local Validation

317

318

Disable local validation when:

319

- Using newer Anthropic API features not yet supported by SDK version

320

- Local validation incorrectly rejects valid schemas (version mismatch)

321

- Debugging schema compatibility issues

322

323

**Disable validation:**

324

```java

325

StructuredMessageCreateParams<BookList> params = MessageCreateParams.builder()

326

.model(Model.CLAUDE_SONNET_4_5_20250929)

327

.maxTokens(2048)

328

.outputFormat(BookList.class, JsonSchemaLocalValidation.NO)

329

.addUserMessage("List famous novels.")

330

.build();

331

```

332

333

## Jackson Annotations

334

335

Jackson Databind annotations control schema generation and add descriptive metadata.

336

337

### @JsonClassDescription

338

339

Adds a description to a class in the generated schema.

340

341

```java { .api }

342

@Target(ElementType.TYPE)

343

@Retention(RetentionPolicy.RUNTIME)

344

public @interface JsonClassDescription {

345

String value();

346

}

347

```

348

349

**Example:**

350

```java

351

import com.fasterxml.jackson.annotation.JsonClassDescription;

352

353

@JsonClassDescription("The details of one published book")

354

class Book {

355

public String title;

356

public Person author;

357

public int publicationYear;

358

}

359

```

360

361

### @JsonPropertyDescription

362

363

Adds a description to a field or getter method in the generated schema.

364

365

```java { .api }

366

@Target({ElementType.FIELD, ElementType.METHOD})

367

@Retention(RetentionPolicy.RUNTIME)

368

public @interface JsonPropertyDescription {

369

String value();

370

}

371

```

372

373

**Example:**

374

```java

375

import com.fasterxml.jackson.annotation.JsonPropertyDescription;

376

377

class Person {

378

@JsonPropertyDescription("The first name and surname of the person")

379

public String name;

380

381

public int birthYear;

382

383

@JsonPropertyDescription("The year the person died, or 'present' if the person is living")

384

public String deathYear;

385

}

386

```

387

388

### @JsonIgnore

389

390

Excludes a public field or getter method from the generated schema.

391

392

```java { .api }

393

@Target({ElementType.FIELD, ElementType.METHOD})

394

@Retention(RetentionPolicy.RUNTIME)

395

public @interface JsonIgnore {

396

boolean value() default true;

397

}

398

```

399

400

**Example:**

401

```java

402

import com.fasterxml.jackson.annotation.JsonIgnore;

403

404

class Book {

405

public String title;

406

public Person author;

407

public int publicationYear;

408

409

@JsonIgnore

410

public String internalNotes; // Excluded from schema

411

}

412

```

413

414

### @JsonProperty

415

416

Includes a non-public field or getter method in the generated schema, or customizes property names.

417

418

```java { .api }

419

@Target({ElementType.FIELD, ElementType.METHOD})

420

@Retention(RetentionPolicy.RUNTIME)

421

public @interface JsonProperty {

422

String value() default "";

423

boolean required() default true;

424

}

425

```

426

427

**Example:**

428

```java

429

import com.fasterxml.jackson.annotation.JsonProperty;

430

431

class Book {

432

@JsonProperty

433

private String title; // Included despite being private

434

435

private String author;

436

437

@JsonProperty("author_name")

438

public String getAuthor() { // Property named "author_name"

439

return author;

440

}

441

}

442

```

443

444

**Note:** The `required` attribute is ignored. Anthropic schemas require all properties to be marked as required (unless wrapped in `Optional<T>`).

445

446

## Swagger Annotations

447

448

Swagger/OpenAPI annotations add type-specific constraints to schema properties.

449

450

### @Schema

451

452

Adds constraints and metadata to fields and classes.

453

454

```java { .api }

455

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})

456

@Retention(RetentionPolicy.RUNTIME)

457

public @interface Schema {

458

String description() default "";

459

String format() default "";

460

String minimum() default "";

461

String maximum() default "";

462

String pattern() default "";

463

int minLength() default Integer.MIN_VALUE;

464

int maxLength() default Integer.MAX_VALUE;

465

}

466

```

467

468

**Supported constraints:**

469

- `description` - Property or class description

470

- `format` - String format (e.g., "date", "date-time", "email", "uri")

471

- `minimum` - Minimum numeric value (as string)

472

- `maximum` - Maximum numeric value (as string)

473

- `pattern` - Regular expression pattern for strings

474

- `minLength` - Minimum string length

475

- `maxLength` - Maximum string length

476

477

**Example:**

478

```java

479

import io.swagger.v3.oas.annotations.media.Schema;

480

481

class Article {

482

public String title;

483

484

@Schema(format = "date")

485

public String publicationDate;

486

487

@Schema(minimum = "1", maximum = "10000")

488

public int pageCount;

489

490

@Schema(pattern = "^[A-Z]{3}-\\d{4}$")

491

public String articleCode;

492

493

@Schema(minLength = 10, maxLength = 500)

494

public String summary;

495

}

496

```

497

498

### @ArraySchema

499

500

Adds array-specific constraints to collection fields.

501

502

```java { .api }

503

@Target({ElementType.FIELD, ElementType.METHOD})

504

@Retention(RetentionPolicy.RUNTIME)

505

public @interface ArraySchema {

506

int minItems() default Integer.MIN_VALUE;

507

int maxItems() default Integer.MAX_VALUE;

508

boolean uniqueItems() default false;

509

}

510

```

511

512

**Example:**

513

```java

514

import io.swagger.v3.oas.annotations.media.ArraySchema;

515

516

class Article {

517

@ArraySchema(minItems = 1, maxItems = 10)

518

public List<String> authors;

519

520

@ArraySchema(uniqueItems = true)

521

public List<String> tags;

522

523

public String title;

524

}

525

```

526

527

**Annotation precedence:** When both Jackson and Swagger annotations set the same schema field, Jackson annotations take precedence. For example:

528

529

```java

530

import com.fasterxml.jackson.annotation.JsonPropertyDescription;

531

import io.swagger.v3.oas.annotations.media.Schema;

532

533

class MyObject {

534

@Schema(description = "Swagger description")

535

@JsonPropertyDescription("Jackson description")

536

public String myProperty; // Description will be "Jackson description"

537

}

538

```

539

540

## Streaming Integration

541

542

Structured outputs work with streaming by accumulating JSON strings from stream events and deserializing them into Java objects after the stream completes.

543

544

### BetaMessageAccumulator

545

546

Helper class for accumulating streaming events into a complete message.

547

548

```java { .api }

549

public final class BetaMessageAccumulator {

550

public BetaMessageAccumulator();

551

552

public void accumulate(RawMessageStreamEvent event);

553

public BetaMessage message();

554

public <T> StructuredMessage<T> message(Class<T> outputClass);

555

}

556

```

557

558

**Streaming example:**

559

```java

560

import com.anthropic.helpers.BetaMessageAccumulator;

561

import com.anthropic.models.beta.messages.StructuredMessageCreateParams;

562

import com.anthropic.core.http.StreamResponse;

563

564

// Create structured output parameters

565

StructuredMessageCreateParams<BookList> params = MessageCreateParams.builder()

566

.model(Model.CLAUDE_SONNET_4_5_20250929)

567

.maxTokens(2048)

568

.outputFormat(BookList.class)

569

.addUserMessage("List famous novels from the 20th century.")

570

.build();

571

572

// Create streaming request

573

StreamResponse<RawMessageStreamEvent> stream =

574

client.beta().messages().createStreaming(params);

575

576

// Accumulate events

577

BetaMessageAccumulator accumulator = new BetaMessageAccumulator();

578

stream.stream().forEach(event -> {

579

accumulator.accumulate(event);

580

581

// Process events as they arrive

582

if (event.isContentBlockDelta()) {

583

System.out.print(event.asContentBlockDelta().delta().text());

584

}

585

});

586

587

// Get structured message after streaming completes

588

StructuredMessage<BookList> message = accumulator.message(BookList.class);

589

BookList books = message.getStructuredContent();

590

591

System.out.println("\n\nComplete book list:");

592

books.books.forEach(book ->

593

System.out.println(book.title + " by " + book.author.name)

594

);

595

```

596

597

## Generic Type Erasure

598

599

Java's generic type erasure prevents runtime type information from being available for local variables and parameters. This limits schema derivation to class fields only.

600

601

**Works (field with generic type):**

602

```java

603

class BookList {

604

public List<Book> books; // Generic type preserved in field metadata

605

}

606

607

StructuredMessageCreateParams<BookList> params = MessageCreateParams.builder()

608

.outputFormat(BookList.class) // Can derive schema from BookList.books field

609

.build();

610

```

611

612

**Does NOT work (local variable with generic type):**

613

```java

614

List<Book> books = new ArrayList<>();

615

616

StructuredMessageCreateParams<List<Book>> params = MessageCreateParams.builder()

617

.outputFormat(books.getClass()) // Type erasure: only knows it's a List, not List<Book>

618

.build(); // Cannot generate valid schema

619

```

620

621

**Solution:** Always wrap collections in a class with typed fields:

622

```java

623

class BookCollection {

624

public List<Book> items;

625

}

626

627

StructuredMessageCreateParams<BookCollection> params = MessageCreateParams.builder()

628

.outputFormat(BookCollection.class)

629

.build();

630

```

631

632

## Error Handling

633

634

### JSON Conversion Errors

635

636

When JSON response cannot be converted to the specified Java class, an exception is thrown with the JSON response included for diagnosis.

637

638

**Common causes:**

639

- Response truncated (reached max_tokens limit)

640

- JSON syntax errors in response

641

- Type mismatches between schema and response

642

- Missing required fields in response

643

644

**Example error:**

645

```

646

Failed to deserialize JSON response into class BookList

647

JSON response: {"books":[{"title":"1984","author":{"name":"George Orwell"

648

```

649

650

**Security consideration:** Error messages include the JSON response, which may contain sensitive information. Avoid logging errors directly in production environments, or redact sensitive data first.

651

652

### Schema Validation Errors

653

654

Local validation errors occur when the derived schema violates Anthropic's restrictions.

655

656

**Common causes:**

657

- Unsupported data types (e.g., `LocalDate` without custom serializer)

658

- Classes with no properties (all fields excluded or no fields defined)

659

- Map types used as field types

660

- Unsupported constraint values

661

662

**Example error:**

663

```

664

Schema validation failed for class Book:

665

- Property 'publishDate' has unsupported type 'java.time.LocalDate'

666

- Use String with @Schema(format="date") instead

667

```

668

669

**Resolution:** Either fix the class definition or disable local validation with `JsonSchemaLocalValidation.NO` if using newer API features.

670

671

## Best Practices

672

673

### Field Definition Patterns

674

675

**Prefer public fields for simple data classes:**

676

```java

677

class Book {

678

public String title;

679

public String author;

680

public int year;

681

}

682

```

683

684

**Use private fields with getters for encapsulation:**

685

```java

686

class Book {

687

private String title;

688

private String author;

689

690

public String getTitle() { return title; }

691

public String getAuthor() { return author; }

692

}

693

```

694

695

**Use `Optional<T>` sparingly:**

696

```java

697

class Book {

698

public String title; // Core data: required

699

public String author; // Core data: required

700

public Optional<String> isbn; // Truly optional metadata

701

}

702

```

703

704

### Schema Validation

705

706

**Let local validation catch errors early:**

707

```java

708

try {

709

StructuredMessageCreateParams<Book> params = MessageCreateParams.builder()

710

.outputFormat(Book.class) // Validates schema immediately

711

.build();

712

} catch (AnthropicException e) {

713

System.err.println("Invalid schema: " + e.getMessage());

714

// Fix class definition before making API calls

715

}

716

```

717

718

**Disable validation only when necessary:**

719

```java

720

// Only use when local validation is incorrect

721

params = MessageCreateParams.builder()

722

.outputFormat(Book.class, JsonSchemaLocalValidation.NO)

723

.build();

724

```

725

726

### Annotations

727

728

**Add descriptions for better AI responses:**

729

```java

730

@JsonClassDescription("A published book with author and publication information")

731

class Book {

732

@JsonPropertyDescription("The full title of the book")

733

public String title;

734

735

@JsonPropertyDescription("The primary author of the book")

736

public Person author;

737

738

@JsonPropertyDescription("The year the book was first published")

739

public int publicationYear;

740

}

741

```

742

743

**Add constraints for validation:**

744

```java

745

class Article {

746

@Schema(minLength = 1, maxLength = 200)

747

public String title;

748

749

@ArraySchema(minItems = 1, maxItems = 10)

750

public List<String> authors;

751

752

@Schema(minimum = "1", maximum = "1000")

753

public int pageCount;

754

}

755

```

756

757

### Complete Example

758

759

**Define data classes:**

760

```java

761

import com.fasterxml.jackson.annotation.JsonClassDescription;

762

import com.fasterxml.jackson.annotation.JsonPropertyDescription;

763

import io.swagger.v3.oas.annotations.media.ArraySchema;

764

import io.swagger.v3.oas.annotations.media.Schema;

765

import java.util.List;

766

import java.util.Optional;

767

768

@JsonClassDescription("A person with biographical information")

769

class Person {

770

@JsonPropertyDescription("The full name of the person")

771

public String name;

772

773

@Schema(minimum = "1800", maximum = "2100")

774

public int birthYear;

775

776

@JsonPropertyDescription("The year the person died, or empty if still living")

777

public Optional<Integer> deathYear;

778

}

779

780

@JsonClassDescription("A published book with metadata")

781

class Book {

782

@JsonPropertyDescription("The full title of the book")

783

@Schema(minLength = 1, maxLength = 200)

784

public String title;

785

786

@JsonPropertyDescription("The primary author of the book")

787

public Person author;

788

789

@Schema(minimum = "1800", maximum = "2100")

790

public int publicationYear;

791

792

@Schema(pattern = "^(?:ISBN(?:-13)?:?\\ )?(?=[0-9]{13}$|(?=(?:[0-9]+[-\\ ]){4})[-\\ 0-9]{17}$)97[89][-\\ ]?[0-9]{1,5}[-\\ ]?[0-9]+[-\\ ]?[0-9]+[-\\ ]?[0-9]$")

793

public Optional<String> isbn;

794

}

795

796

@JsonClassDescription("A collection of books")

797

class BookList {

798

@ArraySchema(minItems = 1, maxItems = 20)

799

public List<Book> books;

800

}

801

```

802

803

**Create and execute request:**

804

```java

805

import com.anthropic.client.AnthropicClient;

806

import com.anthropic.client.okhttp.AnthropicOkHttpClient;

807

import com.anthropic.models.beta.messages.MessageCreateParams;

808

import com.anthropic.models.beta.messages.StructuredMessage;

809

import com.anthropic.models.beta.messages.StructuredMessageCreateParams;

810

import com.anthropic.models.messages.Model;

811

812

AnthropicClient client = AnthropicOkHttpClient.fromEnv();

813

814

StructuredMessageCreateParams<BookList> params = MessageCreateParams.builder()

815

.model(Model.CLAUDE_SONNET_4_5_20250929)

816

.maxTokens(4096)

817

.outputFormat(BookList.class)

818

.addUserMessage("List 5 famous novels from the late 20th century (1950-2000).")

819

.build();

820

821

try {

822

StructuredMessage<BookList> response = client.beta().messages().create(params);

823

BookList result = response.getStructuredContent();

824

825

System.out.println("Famous 20th century novels:");

826

result.books.forEach(book -> {

827

System.out.printf("%s by %s (%d)%n",

828

book.title,

829

book.author.name,

830

book.publicationYear);

831

book.isbn.ifPresent(isbn -> System.out.println(" ISBN: " + isbn));

832

});

833

} catch (AnthropicException e) {

834

System.err.println("Error: " + e.getMessage());

835

}

836

```

837