or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application.mdconfiguration.mdcontext.mdconversion.mdindex.mdjson.mdlogging.mdweb.md

conversion.mddocs/

0

# Type Conversion

1

2

Spring Boot's type conversion system extends Spring's core ConversionService with additional converters for common data types including durations, data sizes, periods, and collections. It provides formatted conversion with annotations and support for property binding.

3

4

## Capabilities

5

6

### Application Conversion Service

7

8

Enhanced conversion service that includes all Spring Boot converters and formatters.

9

10

```java { .api }

11

/**

12

* A specialization of DefaultFormattingConversionService configured by default with

13

* converters and formatters appropriate for most Spring Boot applications

14

*/

15

public class ApplicationConversionService extends DefaultFormattingConversionService {

16

17

private static volatile ApplicationConversionService sharedInstance;

18

19

/**

20

* Return a shared default application ConversionService instance, lazily

21

* building it once needed

22

* @return the shared ApplicationConversionService instance (never null)

23

*/

24

public static ConversionService getSharedInstance() {

25

ApplicationConversionService sharedInstance = ApplicationConversionService.sharedInstance;

26

if (sharedInstance == null) {

27

synchronized (ApplicationConversionService.class) {

28

sharedInstance = ApplicationConversionService.sharedInstance;

29

if (sharedInstance == null) {

30

sharedInstance = new ApplicationConversionService();

31

ApplicationConversionService.sharedInstance = sharedInstance;

32

}

33

}

34

}

35

return sharedInstance;

36

}

37

38

/**

39

* Configure the given FormatterRegistry with formatters and converters appropriate

40

* for most Spring Boot applications

41

* @param registry the registry of converters to add to (must not be null)

42

*/

43

public static void configure(FormatterRegistry registry) {

44

DefaultConversionService.addDefaultConverters(registry);

45

DefaultFormattingConversionService.addDefaultFormatters(registry);

46

addApplicationFormatters(registry);

47

addApplicationConverters(registry);

48

}

49

50

/**

51

* Add converters useful for most Spring Boot applications

52

* @param registry the registry of converters to add to (must not be null)

53

*/

54

public static void addApplicationConverters(ConverterRegistry registry) {

55

addDelimitedStringConverters(registry);

56

registry.addConverter(new StringToDurationConverter());

57

registry.addConverter(new DurationToStringConverter());

58

registry.addConverter(new NumberToDurationConverter());

59

registry.addConverter(new DurationToNumberConverter());

60

registry.addConverter(new StringToPeriodConverter());

61

registry.addConverter(new PeriodToStringConverter());

62

registry.addConverter(new NumberToPeriodConverter());

63

registry.addConverter(new StringToDataSizeConverter());

64

registry.addConverter(new NumberToDataSizeConverter());

65

registry.addConverter(new StringToFileConverter());

66

registry.addConverter(new InputStreamSourceToByteArrayConverter());

67

}

68

69

/**

70

* Add formatters useful for most Spring Boot applications

71

* @param registry the service to register default formatters with

72

*/

73

public static void addApplicationFormatters(FormatterRegistry registry) {

74

registry.addFormatter(new CharArrayFormatter());

75

registry.addFormatter(new InetAddressFormatter());

76

registry.addFormatter(new IsoOffsetFormatter());

77

}

78

79

private static void addDelimitedStringConverters(ConverterRegistry registry) {

80

ConversionService service = (ConversionService) registry;

81

registry.addConverter(new ArrayToDelimitedStringConverter(service));

82

registry.addConverter(new CollectionToDelimitedStringConverter(service));

83

registry.addConverter(new DelimitedStringToArrayConverter(service));

84

registry.addConverter(new DelimitedStringToCollectionConverter(service));

85

}

86

}

87

```

88

89

**Usage Examples:**

90

91

```java

92

import org.springframework.context.annotation.Configuration;

93

import org.springframework.context.annotation.Bean;

94

import org.springframework.context.annotation.Primary;

95

import org.springframework.core.convert.ConversionService;

96

97

@Configuration

98

public class ConversionConfiguration {

99

100

@Bean

101

@Primary

102

public ConversionService conversionService() {

103

return ApplicationConversionService.getSharedInstance();

104

}

105

}

106

107

@Service

108

public class DataConverter {

109

110

@Autowired

111

private ConversionService conversionService;

112

113

public void demonstrateConversions() {

114

// Duration conversions

115

Duration duration = conversionService.convert("30s", Duration.class);

116

String durationStr = conversionService.convert(Duration.ofMinutes(5), String.class);

117

118

// Data size conversions

119

DataSize size = conversionService.convert("10MB", DataSize.class);

120

String sizeStr = conversionService.convert(DataSize.ofGigabytes(2), String.class);

121

122

// Period conversions

123

Period period = conversionService.convert("P1Y2M3D", Period.class);

124

125

// Collection conversions with delimiters

126

List<String> list = conversionService.convert("a,b,c",

127

TypeDescriptor.valueOf(String.class),

128

TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));

129

130

System.out.println("Duration: " + duration);

131

System.out.println("Duration string: " + durationStr);

132

System.out.println("Data size: " + size);

133

System.out.println("List: " + list);

134

}

135

}

136

```

137

138

### Duration Support

139

140

Duration conversion with multiple format styles and units.

141

142

```java { .api }

143

/**

144

* Format annotation for Duration conversion

145

*/

146

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

147

@Retention(RetentionPolicy.RUNTIME)

148

@Documented

149

public @interface DurationFormat {

150

151

/**

152

* The duration format style to use

153

* @return the duration style

154

*/

155

DurationStyle value();

156

157

/**

158

* The duration unit to use when the format style is SIMPLE

159

* @return the duration unit

160

*/

161

ChronoUnit unit() default ChronoUnit.MILLIS;

162

}

163

164

/**

165

* Duration format styles

166

*/

167

public enum DurationStyle implements TemporalUnitStyleFormatter {

168

169

/**

170

* Simple formatting, for example '1s'

171

*/

172

SIMPLE("^([+\\-]?\\d+)([a-zA-Z]{0,2})$") {

173

@Override

174

public Duration parse(String value, ChronoUnit unit) {

175

try {

176

Matcher matcher = getPattern().matcher(value);

177

Assert.state(matcher.matches(), "Does not match simple duration pattern");

178

String suffix = matcher.group(2);

179

return (StringUtils.hasLength(suffix) ? Unit.fromSuffix(suffix) : Unit.fromChronoUnit(unit))

180

.parse(matcher.group(1));

181

}

182

catch (Exception ex) {

183

throw new IllegalArgumentException("'" + value + "' is not a valid simple duration", ex);

184

}

185

}

186

187

@Override

188

public String print(Duration value, ChronoUnit unit) {

189

return Unit.fromChronoUnit(unit).print(value);

190

}

191

},

192

193

/**

194

* ISO-8601 formatting

195

*/

196

ISO8601("^[+\\-]?P.*$") {

197

@Override

198

public Duration parse(String value, ChronoUnit unit) {

199

try {

200

return Duration.parse(value);

201

}

202

catch (Exception ex) {

203

throw new IllegalArgumentException("'" + value + "' is not a valid ISO-8601 duration", ex);

204

}

205

}

206

207

@Override

208

public String print(Duration value, ChronoUnit unit) {

209

return value.toString();

210

}

211

};

212

213

/**

214

* Parse the given value to a duration

215

* @param value the value to parse

216

* @return a duration

217

*/

218

public Duration parse(String value) {

219

return parse(value, null);

220

}

221

222

/**

223

* Parse the given value to a duration using the specified unit when the value

224

* doesn't contain a suffix

225

* @param value the value to parse

226

* @param unit the duration unit to use when there is no suffix

227

* @return a duration

228

*/

229

public abstract Duration parse(String value, ChronoUnit unit);

230

231

/**

232

* Print the specified duration

233

* @param value the value to print

234

* @param unit the value to use for printing when the original unit is not known

235

* @return the printed result

236

*/

237

public abstract String print(Duration value, ChronoUnit unit);

238

239

/**

240

* Detect the style from the given source value

241

* @param value the source value

242

* @return the duration style

243

*/

244

public static DurationStyle detect(String value) {

245

Assert.notNull(value, "Value must not be null");

246

for (DurationStyle candidate : values()) {

247

if (candidate.matches(value)) {

248

return candidate;

249

}

250

}

251

throw new IllegalArgumentException("'" + value + "' is not a valid duration");

252

}

253

}

254

```

255

256

**Usage Examples:**

257

258

```java

259

@ConfigurationProperties("app.timeout")

260

public class TimeoutProperties {

261

262

@DurationFormat(DurationStyle.SIMPLE)

263

private Duration connectionTimeout = Duration.ofSeconds(30);

264

265

@DurationFormat(value = DurationStyle.SIMPLE, unit = ChronoUnit.MINUTES)

266

private Duration sessionTimeout = Duration.ofMinutes(30);

267

268

@DurationFormat(DurationStyle.ISO8601)

269

private Duration maxWaitTime = Duration.parse("PT1M30S");

270

271

// Getters and setters

272

public Duration getConnectionTimeout() { return connectionTimeout; }

273

public void setConnectionTimeout(Duration connectionTimeout) {

274

this.connectionTimeout = connectionTimeout;

275

}

276

277

public Duration getSessionTimeout() { return sessionTimeout; }

278

public void setSessionTimeout(Duration sessionTimeout) {

279

this.sessionTimeout = sessionTimeout;

280

}

281

282

public Duration getMaxWaitTime() { return maxWaitTime; }

283

public void setMaxWaitTime(Duration maxWaitTime) {

284

this.maxWaitTime = maxWaitTime;

285

}

286

}

287

288

// Properties file:

289

// app.timeout.connection-timeout=5s

290

// app.timeout.session-timeout=30

291

// app.timeout.max-wait-time=PT2M30S

292

293

@Service

294

public class DurationService {

295

296

public void demonstrateDurationParsing() {

297

// Simple format parsing

298

Duration duration1 = DurationStyle.SIMPLE.parse("30s");

299

Duration duration2 = DurationStyle.SIMPLE.parse("5", ChronoUnit.MINUTES);

300

Duration duration3 = DurationStyle.SIMPLE.parse("2h");

301

302

// ISO-8601 format parsing

303

Duration duration4 = DurationStyle.ISO8601.parse("PT30M");

304

Duration duration5 = DurationStyle.ISO8601.parse("P1DT2H30M");

305

306

// Auto-detection

307

Duration duration6 = DurationStyle.detect("45s").parse("45s");

308

Duration duration7 = DurationStyle.detect("PT1H").parse("PT1H");

309

310

System.out.println("30s = " + duration1);

311

System.out.println("5 minutes = " + duration2);

312

System.out.println("2h = " + duration3);

313

System.out.println("PT30M = " + duration4);

314

System.out.println("P1DT2H30M = " + duration5);

315

}

316

}

317

```

318

319

### Data Size Support

320

321

Data size conversion with standard units (bytes, KB, MB, GB, TB).

322

323

```java { .api }

324

/**

325

* A data size, such as '12MB'

326

*/

327

public final class DataSize implements Comparable<DataSize>, Serializable {

328

329

/**

330

* Bytes per Kilobyte

331

*/

332

public static final long BYTES_PER_KB = 1024;

333

334

/**

335

* Bytes per Megabyte

336

*/

337

public static final long BYTES_PER_MB = BYTES_PER_KB * 1024;

338

339

/**

340

* Bytes per Gigabyte

341

*/

342

public static final long BYTES_PER_GB = BYTES_PER_MB * 1024;

343

344

/**

345

* Bytes per Terabyte

346

*/

347

public static final long BYTES_PER_TB = BYTES_PER_GB * 1024;

348

349

/**

350

* Obtain a DataSize representing the specified number of bytes

351

* @param bytes the number of bytes, positive or negative

352

* @return a DataSize

353

*/

354

public static DataSize ofBytes(long bytes) {

355

return new DataSize(bytes);

356

}

357

358

/**

359

* Obtain a DataSize representing the specified number of kilobytes

360

* @param kilobytes the number of kilobytes, positive or negative

361

* @return a DataSize

362

*/

363

public static DataSize ofKilobytes(long kilobytes) {

364

return new DataSize(Math.multiplyExact(kilobytes, BYTES_PER_KB));

365

}

366

367

/**

368

* Obtain a DataSize representing the specified number of megabytes

369

* @param megabytes the number of megabytes, positive or negative

370

* @return a DataSize

371

*/

372

public static DataSize ofMegabytes(long megabytes) {

373

return new DataSize(Math.multiplyExact(megabytes, BYTES_PER_MB));

374

}

375

376

/**

377

* Obtain a DataSize representing the specified number of gigabytes

378

* @param gigabytes the number of gigabytes, positive or negative

379

* @return a DataSize

380

*/

381

public static DataSize ofGigabytes(long gigabytes) {

382

return new DataSize(Math.multiplyExact(gigabytes, BYTES_PER_GB));

383

}

384

385

/**

386

* Obtain a DataSize representing the specified number of terabytes

387

* @param terabytes the number of terabytes, positive or negative

388

* @return a DataSize

389

*/

390

public static DataSize ofTerabytes(long terabytes) {

391

return new DataSize(Math.multiplyExact(terabytes, BYTES_PER_TB));

392

}

393

394

/**

395

* Obtain a DataSize from a text string such as {@code 12MB}

396

* @param text the text to parse

397

* @return the parsed DataSize

398

*/

399

public static DataSize parse(CharSequence text) {

400

return parse(text, null);

401

}

402

403

/**

404

* Obtain a DataSize from a text string such as {@code 12MB}

405

* @param text the text to parse

406

* @param defaultUnit the default DataUnit if none is specified

407

* @return the parsed DataSize

408

*/

409

public static DataSize parse(CharSequence text, DataUnit defaultUnit) {

410

Assert.notNull(text, "Text must not be null");

411

try {

412

Matcher matcher = PATTERN.matcher(text);

413

Assert.state(matcher.matches(), "Does not match data size pattern");

414

DataUnit unit = determineDataUnit(matcher.group(2), defaultUnit);

415

long amount = Long.parseLong(matcher.group(1));

416

return DataSize.of(amount, unit);

417

}

418

catch (Exception ex) {

419

throw new IllegalArgumentException("'" + text + "' is not a valid data size", ex);

420

}

421

}

422

423

/**

424

* Return the number of bytes in this instance

425

* @return the number of bytes

426

*/

427

public long toBytes() {

428

return this.bytes;

429

}

430

431

/**

432

* Return the number of kilobytes in this instance

433

* @return the number of kilobytes

434

*/

435

public long toKilobytes() {

436

return this.bytes / BYTES_PER_KB;

437

}

438

439

/**

440

* Return the number of megabytes in this instance

441

* @return the number of megabytes

442

*/

443

public long toMegabytes() {

444

return this.bytes / BYTES_PER_MB;

445

}

446

447

/**

448

* Return the number of gigabytes in this instance

449

* @return the number of gigabytes

450

*/

451

public long toGigabytes() {

452

return this.bytes / BYTES_PER_GB;

453

}

454

455

/**

456

* Return the number of terabytes in this instance

457

* @return the number of terabytes

458

*/

459

public long toTerabytes() {

460

return this.bytes / BYTES_PER_TB;

461

}

462

}

463

464

/**

465

* A standard set of DataSize units

466

*/

467

public enum DataUnit {

468

469

/**

470

* Bytes

471

*/

472

BYTES("B", DataSize.ofBytes(1)),

473

474

/**

475

* Kilobytes

476

*/

477

KILOBYTES("KB", DataSize.ofKilobytes(1)),

478

479

/**

480

* Megabytes

481

*/

482

MEGABYTES("MB", DataSize.ofMegabytes(1)),

483

484

/**

485

* Gigabytes

486

*/

487

GIGABYTES("GB", DataSize.ofGigabytes(1)),

488

489

/**

490

* Terabytes

491

*/

492

TERABYTES("TB", DataSize.ofTerabytes(1));

493

494

private final String suffix;

495

private final DataSize size;

496

497

DataUnit(String suffix, DataSize size) {

498

this.suffix = suffix;

499

this.size = size;

500

}

501

502

DataSize size() {

503

return this.size;

504

}

505

506

/**

507

* Return the DataUnit matching the specified suffix

508

* @param suffix the suffix to match

509

* @return the matching DataUnit

510

*/

511

public static DataUnit fromSuffix(String suffix) {

512

for (DataUnit candidate : values()) {

513

if (candidate.suffix.equalsIgnoreCase(suffix)) {

514

return candidate;

515

}

516

}

517

throw new IllegalArgumentException("Unknown data unit suffix '" + suffix + "'");

518

}

519

}

520

```

521

522

**Usage Examples:**

523

524

```java

525

@ConfigurationProperties("app.storage")

526

public class StorageProperties {

527

528

private DataSize maxFileSize = DataSize.ofMegabytes(10);

529

private DataSize totalQuota = DataSize.ofGigabytes(100);

530

private DataSize cacheSize = DataSize.ofMegabytes(256);

531

532

// Getters and setters

533

public DataSize getMaxFileSize() { return maxFileSize; }

534

public void setMaxFileSize(DataSize maxFileSize) { this.maxFileSize = maxFileSize; }

535

536

public DataSize getTotalQuota() { return totalQuota; }

537

public void setTotalQuota(DataSize totalQuota) { this.totalQuota = totalQuota; }

538

539

public DataSize getCacheSize() { return cacheSize; }

540

public void setCacheSize(DataSize cacheSize) { this.cacheSize = cacheSize; }

541

}

542

543

// Properties file:

544

// app.storage.max-file-size=50MB

545

// app.storage.total-quota=2GB

546

// app.storage.cache-size=512MB

547

548

@Service

549

public class DataSizeService {

550

551

public void demonstrateDataSizeParsing() {

552

// Parsing different formats

553

DataSize size1 = DataSize.parse("10MB");

554

DataSize size2 = DataSize.parse("2GB");

555

DataSize size3 = DataSize.parse("1024KB");

556

DataSize size4 = DataSize.parse("500"); // Bytes by default

557

DataSize size5 = DataSize.parse("500", DataUnit.KILOBYTES);

558

559

// Creating sizes programmatically

560

DataSize size6 = DataSize.ofBytes(1024);

561

DataSize size7 = DataSize.ofMegabytes(50);

562

DataSize size8 = DataSize.ofGigabytes(2);

563

564

// Converting between units

565

System.out.println("10MB in bytes: " + size1.toBytes());

566

System.out.println("2GB in MB: " + size2.toMegabytes());

567

System.out.println("1024KB in MB: " + size3.toMegabytes());

568

569

// Comparisons

570

System.out.println("10MB > 5MB: " + (size1.compareTo(DataSize.ofMegabytes(5)) > 0));

571

}

572

}

573

```

574

575

### Period Support

576

577

Period conversion for date-based amounts of time.

578

579

```java { .api }

580

/**

581

* Format annotation for Period conversion

582

*/

583

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

584

@Retention(RetentionPolicy.RUNTIME)

585

@Documented

586

public @interface PeriodFormat {

587

588

/**

589

* The period format style to use

590

* @return the period style

591

*/

592

PeriodStyle value();

593

}

594

595

/**

596

* Period format styles

597

*/

598

public enum PeriodStyle {

599

600

/**

601

* Simple formatting, for example '1y2m3d'

602

*/

603

SIMPLE("^([+\\-]?\\d+)([ymdw])$") {

604

@Override

605

public Period parse(String value, ChronoUnit unit) {

606

try {

607

if (value.isEmpty()) {

608

return Period.ZERO;

609

}

610

Matcher matcher = getPattern().matcher(value.toLowerCase());

611

Period period = Period.ZERO;

612

while (matcher.find()) {

613

String number = matcher.group(1);

614

String suffix = matcher.group(2);

615

Unit valueUnit = Unit.fromSuffix(suffix);

616

period = period.plus(valueUnit.parse(number));

617

}

618

return period;

619

}

620

catch (Exception ex) {

621

throw new IllegalArgumentException("'" + value + "' is not a valid simple period", ex);

622

}

623

}

624

625

@Override

626

public String print(Period value, ChronoUnit unit) {

627

if (value.isZero()) {

628

return "0d";

629

}

630

StringBuilder result = new StringBuilder();

631

if (value.getYears() != 0) {

632

result.append(value.getYears()).append("y");

633

}

634

if (value.getMonths() != 0) {

635

result.append(value.getMonths()).append("m");

636

}

637

if (value.getDays() != 0) {

638

result.append(value.getDays()).append("d");

639

}

640

return result.toString();

641

}

642

},

643

644

/**

645

* ISO-8601 formatting

646

*/

647

ISO8601("^[+\\-]?P.*$") {

648

@Override

649

public Period parse(String value, ChronoUnit unit) {

650

try {

651

return Period.parse(value);

652

}

653

catch (Exception ex) {

654

throw new IllegalArgumentException("'" + value + "' is not a valid ISO-8601 period", ex);

655

}

656

}

657

658

@Override

659

public String print(Period value, ChronoUnit unit) {

660

return value.toString();

661

}

662

};

663

664

/**

665

* Parse the given value to a period

666

* @param value the value to parse

667

* @return a period

668

*/

669

public Period parse(String value) {

670

return parse(value, null);

671

}

672

673

/**

674

* Parse the given value to a period

675

* @param value the value to parse

676

* @param unit the period unit (may be null)

677

* @return a period

678

*/

679

public abstract Period parse(String value, ChronoUnit unit);

680

681

/**

682

* Print the specified period

683

* @param value the value to print

684

* @param unit the value to use for printing when the original unit is not known

685

* @return the printed result

686

*/

687

public abstract String print(Period value, ChronoUnit unit);

688

689

/**

690

* Detect the style from the given source value

691

* @param value the source value

692

* @return the period style

693

*/

694

public static PeriodStyle detect(String value) {

695

Assert.notNull(value, "Value must not be null");

696

for (PeriodStyle candidate : values()) {

697

if (candidate.matches(value)) {

698

return candidate;

699

}

700

}

701

throw new IllegalArgumentException("'" + value + "' is not a valid period");

702

}

703

}

704

```

705

706

**Usage Examples:**

707

708

```java

709

@ConfigurationProperties("app.schedule")

710

public class ScheduleProperties {

711

712

@PeriodFormat(PeriodStyle.SIMPLE)

713

private Period retentionPeriod = Period.ofDays(30);

714

715

@PeriodFormat(PeriodStyle.ISO8601)

716

private Period archivePeriod = Period.parse("P1Y");

717

718

@PeriodFormat(PeriodStyle.SIMPLE)

719

private Period cleanupInterval = Period.ofWeeks(2);

720

721

// Getters and setters

722

public Period getRetentionPeriod() { return retentionPeriod; }

723

public void setRetentionPeriod(Period retentionPeriod) {

724

this.retentionPeriod = retentionPeriod;

725

}

726

727

public Period getArchivePeriod() { return archivePeriod; }

728

public void setArchivePeriod(Period archivePeriod) {

729

this.archivePeriod = archivePeriod;

730

}

731

732

public Period getCleanupInterval() { return cleanupInterval; }

733

public void setCleanupInterval(Period cleanupInterval) {

734

this.cleanupInterval = cleanupInterval;

735

}

736

}

737

738

// Properties file:

739

// app.schedule.retention-period=90d

740

// app.schedule.archive-period=P2Y

741

// app.schedule.cleanup-interval=1m

742

```

743

744

### Delimited String Conversion

745

746

Collection conversion with custom delimiters.

747

748

```java { .api }

749

/**

750

* Annotation that can be used to change the delimiter used when converting from a

751

* String

752

*/

753

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

754

@Retention(RetentionPolicy.RUNTIME)

755

@Documented

756

public @interface Delimiter {

757

758

/**

759

* The delimiter to use or an empty string if the delimiter should be inherited

760

* from the field type

761

* @return the delimiter

762

*/

763

String value();

764

}

765

```

766

767

**Usage Examples:**

768

769

```java

770

@ConfigurationProperties("app.lists")

771

public class ListProperties {

772

773

// Default comma delimiter: "apple,banana,cherry"

774

private List<String> fruits;

775

776

// Custom pipe delimiter: "red|green|blue"

777

@Delimiter("|")

778

private List<String> colors;

779

780

// Semicolon delimiter: "admin;user;guest"

781

@Delimiter(";")

782

private Set<String> roles;

783

784

// Space delimiter: "tag1 tag2 tag3"

785

@Delimiter(" ")

786

private String[] tags;

787

788

// Custom delimiter with numbers: "1:2:3:4"

789

@Delimiter(":")

790

private List<Integer> numbers;

791

792

// Getters and setters

793

public List<String> getFruits() { return fruits; }

794

public void setFruits(List<String> fruits) { this.fruits = fruits; }

795

796

public List<String> getColors() { return colors; }

797

public void setColors(List<String> colors) { this.colors = colors; }

798

799

public Set<String> getRoles() { return roles; }

800

public void setRoles(Set<String> roles) { this.roles = roles; }

801

802

public String[] getTags() { return tags; }

803

public void setTags(String[] tags) { this.tags = tags; }

804

805

public List<Integer> getNumbers() { return numbers; }

806

public void setNumbers(List<Integer> numbers) { this.numbers = numbers; }

807

}

808

809

// Properties file:

810

// app.lists.fruits=apple,banana,cherry,orange

811

// app.lists.colors=red|green|blue|yellow

812

// app.lists.roles=admin;user;guest;moderator

813

// app.lists.tags=spring boot java microservices

814

// app.lists.numbers=10:20:30:40:50

815

816

@Service

817

public class DelimiterService {

818

819

@Autowired

820

private ConversionService conversionService;

821

822

public void demonstrateDelimiterConversion() {

823

TypeDescriptor stringType = TypeDescriptor.valueOf(String.class);

824

TypeDescriptor listType = TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class));

825

826

// Convert delimited string to list

827

List<String> list = (List<String>) conversionService.convert(

828

"a,b,c,d", stringType, listType);

829

830

// Convert list back to delimited string

831

String delimited = (String) conversionService.convert(

832

Arrays.asList("x", "y", "z"), listType, stringType);

833

834

System.out.println("List from string: " + list);

835

System.out.println("String from list: " + delimited);

836

}

837

}

838

```