or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdindex.mdjson-arrays.mdjson-objects.mdparsing.mdserialization.mdtypes-exceptions.md

types-exceptions.mddocs/

0

# Type System and Error Handling

1

2

Runtime type checking, safe type conversion, and comprehensive error reporting for invalid input with detailed exception information and type safety utilities.

3

4

## Capabilities

5

6

### Type Checking

7

8

Determine the type of JSON values at runtime with boolean predicates.

9

10

```java { .api }

11

/**

12

* Returns true if this is a JSON object

13

* @return true if this value represents a JSON object

14

*/

15

boolean isObject();

16

17

/**

18

* Returns true if this is a JSON array

19

* @return true if this value represents a JSON array

20

*/

21

boolean isArray();

22

23

/**

24

* Returns true if this is a JSON string

25

* @return true if this value represents a JSON string

26

*/

27

boolean isString();

28

29

/**

30

* Returns true if this is a JSON number

31

* @return true if this value represents a JSON number

32

*/

33

boolean isNumber();

34

35

/**

36

* Returns true if this is a JSON boolean

37

* @return true if this value represents a JSON boolean

38

*/

39

boolean isBoolean();

40

41

/**

42

* Returns true if this is the JSON literal true

43

* @return true if this value represents the JSON literal true

44

*/

45

boolean isTrue();

46

47

/**

48

* Returns true if this is the JSON literal false

49

* @return true if this value represents the JSON literal false

50

*/

51

boolean isFalse();

52

53

/**

54

* Returns true if this is the JSON literal null

55

* @return true if this value represents the JSON literal null

56

*/

57

boolean isNull();

58

59

/**

60

* Returns the type of this JSON value

61

* @return JsonType enum representing the value type

62

*/

63

JsonType getType();

64

```

65

66

**Usage Examples:**

67

68

```java

69

import org.hjson.JsonValue;

70

import org.hjson.JsonType;

71

72

JsonValue value = JsonValue.readHjson("""

73

{

74

name: "John Doe"

75

age: 30

76

active: true

77

spouse: null

78

children: []

79

metadata: {}

80

}

81

""").asObject();

82

83

// Type checking for safe processing

84

for (JsonObject.Member member : value.asObject()) {

85

String name = member.getName();

86

JsonValue memberValue = member.getValue();

87

88

if (memberValue.isString()) {

89

System.out.println(name + " is a string: " + memberValue.asString());

90

} else if (memberValue.isNumber()) {

91

System.out.println(name + " is a number: " + memberValue.asInt());

92

} else if (memberValue.isBoolean()) {

93

System.out.println(name + " is a boolean: " + memberValue.asBoolean());

94

} else if (memberValue.isNull()) {

95

System.out.println(name + " is null");

96

} else if (memberValue.isArray()) {

97

System.out.println(name + " is an array with " + memberValue.asArray().size() + " elements");

98

} else if (memberValue.isObject()) {

99

System.out.println(name + " is an object with " + memberValue.asObject().size() + " members");

100

}

101

102

// Using getType() enum

103

JsonType type = memberValue.getType();

104

System.out.println(name + " type: " + type);

105

}

106

107

// Specific boolean value checking

108

JsonValue boolValue = JsonValue.valueOf(true);

109

if (boolValue.isTrue()) {

110

System.out.println("Value is specifically true");

111

}

112

113

JsonValue falseValue = JsonValue.valueOf(false);

114

if (falseValue.isFalse()) {

115

System.out.println("Value is specifically false");

116

}

117

```

118

119

### Type Conversion

120

121

Convert JSON values to specific Java types with runtime type checking.

122

123

```java { .api }

124

/**

125

* Returns this JSON value as JsonObject

126

* @return this value as JsonObject

127

* @throws UnsupportedOperationException if this value is not a JSON object

128

*/

129

JsonObject asObject();

130

131

/**

132

* Returns this JSON value as JsonArray

133

* @return this value as JsonArray

134

* @throws UnsupportedOperationException if this value is not a JSON array

135

*/

136

JsonArray asArray();

137

138

/**

139

* Returns this JSON value as String

140

* @return string representation of this value

141

* @throws UnsupportedOperationException if this value is not a JSON string

142

*/

143

String asString();

144

145

/**

146

* Returns this JSON value as int

147

* @return int representation of this value

148

* @throws UnsupportedOperationException if this value is not a JSON number

149

* @throws NumberFormatException if the value cannot be represented as int

150

*/

151

int asInt();

152

153

/**

154

* Returns this JSON value as long

155

* @return long representation of this value

156

* @throws UnsupportedOperationException if this value is not a JSON number

157

* @throws NumberFormatException if the value cannot be represented as long

158

*/

159

long asLong();

160

161

/**

162

* Returns this JSON value as float

163

* @return float representation of this value

164

* @throws UnsupportedOperationException if this value is not a JSON number

165

* @throws NumberFormatException if the value cannot be represented as float

166

*/

167

float asFloat();

168

169

/**

170

* Returns this JSON value as double

171

* @return double representation of this value

172

* @throws UnsupportedOperationException if this value is not a JSON number

173

* @throws NumberFormatException if the value cannot be represented as double

174

*/

175

double asDouble();

176

177

/**

178

* Returns this JSON value as boolean

179

* @return boolean representation of this value

180

* @throws UnsupportedOperationException if this value is not a JSON boolean

181

*/

182

boolean asBoolean();

183

184

/**

185

* Returns this JSON value as DSF (Domain Specific Format) object

186

* @return DSF object representation of this value

187

* @throws UnsupportedOperationException if this value is not a DSF value

188

*/

189

Object asDsf();

190

```

191

192

**Usage Examples:**

193

194

```java

195

import org.hjson.JsonValue;

196

import org.hjson.JsonObject;

197

import org.hjson.JsonArray;

198

199

// Safe type conversion with checking

200

JsonValue data = JsonValue.readHjson("""

201

{

202

config: {

203

port: 8080

204

name: "My App"

205

enabled: true

206

}

207

servers: ["server1", "server2", "server3"]

208

version: 1.5

209

}

210

""");

211

212

JsonObject root = data.asObject();

213

214

// Safe object conversion

215

JsonValue configValue = root.get("config");

216

if (configValue.isObject()) {

217

JsonObject config = configValue.asObject();

218

219

// Safe numeric conversion

220

JsonValue portValue = config.get("port");

221

if (portValue.isNumber()) {

222

int port = portValue.asInt();

223

System.out.println("Port: " + port);

224

}

225

226

// Safe string conversion

227

JsonValue nameValue = config.get("name");

228

if (nameValue.isString()) {

229

String name = nameValue.asString();

230

System.out.println("Name: " + name);

231

}

232

233

// Safe boolean conversion

234

JsonValue enabledValue = config.get("enabled");

235

if (enabledValue.isBoolean()) {

236

boolean enabled = enabledValue.asBoolean();

237

System.out.println("Enabled: " + enabled);

238

}

239

}

240

241

// Safe array conversion

242

JsonValue serversValue = root.get("servers");

243

if (serversValue.isArray()) {

244

JsonArray servers = serversValue.asArray();

245

System.out.println("Server count: " + servers.size());

246

247

for (JsonValue serverValue : servers) {

248

if (serverValue.isString()) {

249

System.out.println("Server: " + serverValue.asString());

250

}

251

}

252

}

253

254

// Numeric precision handling

255

JsonValue versionValue = root.get("version");

256

if (versionValue.isNumber()) {

257

double version = versionValue.asDouble();

258

float versionFloat = versionValue.asFloat();

259

System.out.println("Version (double): " + version);

260

System.out.println("Version (float): " + versionFloat);

261

}

262

```

263

264

### Error-Safe Type Conversion

265

266

Handle type conversion errors gracefully with try-catch patterns.

267

268

```java

269

// Error-safe conversion patterns

270

public class SafeConverter {

271

public static String safeAsString(JsonValue value, String defaultValue) {

272

try {

273

return value.isString() ? value.asString() : defaultValue;

274

} catch (UnsupportedOperationException e) {

275

return defaultValue;

276

}

277

}

278

279

public static int safeAsInt(JsonValue value, int defaultValue) {

280

try {

281

return value.isNumber() ? value.asInt() : defaultValue;

282

} catch (UnsupportedOperationException | NumberFormatException e) {

283

return defaultValue;

284

}

285

}

286

287

public static boolean safeAsBoolean(JsonValue value, boolean defaultValue) {

288

try {

289

return value.isBoolean() ? value.asBoolean() : defaultValue;

290

} catch (UnsupportedOperationException e) {

291

return defaultValue;

292

}

293

}

294

295

public static JsonObject safeAsObject(JsonValue value) {

296

try {

297

return value.isObject() ? value.asObject() : null;

298

} catch (UnsupportedOperationException e) {

299

return null;

300

}

301

}

302

}

303

304

// Usage

305

JsonValue unknownValue = getValue(); // Could be any type

306

String stringValue = SafeConverter.safeAsString(unknownValue, "default");

307

int intValue = SafeConverter.safeAsInt(unknownValue, 0);

308

JsonObject objValue = SafeConverter.safeAsObject(unknownValue);

309

if (objValue != null) {

310

// Process object safely

311

}

312

```

313

314

### JsonType Enumeration

315

316

Enumeration of all possible JSON value types.

317

318

```java { .api }

319

/**

320

* Enumeration of JSON value types

321

*/

322

enum JsonType {

323

/**

324

* JSON string value

325

*/

326

STRING,

327

328

/**

329

* JSON number value

330

*/

331

NUMBER,

332

333

/**

334

* JSON object value

335

*/

336

OBJECT,

337

338

/**

339

* JSON array value

340

*/

341

ARRAY,

342

343

/**

344

* JSON boolean value (true or false)

345

*/

346

BOOLEAN,

347

348

/**

349

* JSON null value

350

*/

351

NULL,

352

353

/**

354

* Domain Specific Format value (specialized parsing)

355

*/

356

DSF

357

}

358

```

359

360

**Usage Examples:**

361

362

```java

363

import org.hjson.JsonType;

364

import org.hjson.JsonValue;

365

366

JsonValue mixed = JsonValue.readHjson("""

367

[

368

"text",

369

42,

370

true,

371

null,

372

{ key: "value" },

373

[1, 2, 3]

374

]

375

""").asArray();

376

377

// Process elements based on type

378

for (JsonValue element : mixed.asArray()) {

379

JsonType type = element.getType();

380

381

switch (type) {

382

case STRING:

383

System.out.println("String: " + element.asString());

384

break;

385

case NUMBER:

386

System.out.println("Number: " + element.asDouble());

387

break;

388

case BOOLEAN:

389

System.out.println("Boolean: " + element.asBoolean());

390

break;

391

case NULL:

392

System.out.println("Null value");

393

break;

394

case OBJECT:

395

System.out.println("Object with " + element.asObject().size() + " members");

396

break;

397

case ARRAY:

398

System.out.println("Array with " + element.asArray().size() + " elements");

399

break;

400

case DSF:

401

System.out.println("DSF value: " + element.asDsf());

402

break;

403

}

404

}

405

406

// Type-based filtering

407

public static List<JsonValue> filterByType(JsonArray array, JsonType targetType) {

408

List<JsonValue> filtered = new ArrayList<>();

409

for (JsonValue element : array) {

410

if (element.getType() == targetType) {

411

filtered.add(element);

412

}

413

}

414

return filtered;

415

}

416

417

// Usage

418

List<JsonValue> strings = filterByType(mixed.asArray(), JsonType.STRING);

419

List<JsonValue> numbers = filterByType(mixed.asArray(), JsonType.NUMBER);

420

```

421

422

## Error Handling

423

424

### ParseException

425

426

Exception thrown when parsing invalid JSON or Hjson input.

427

428

```java { .api }

429

/**

430

* Unchecked exception that indicates a problem while parsing JSON or Hjson text

431

* Provides detailed information about the location and nature of the parsing error

432

*/

433

class ParseException extends RuntimeException {

434

/**

435

* Returns the character offset at which the error occurred

436

* @return the character offset (0-based) where the error was detected

437

*/

438

int getOffset();

439

440

/**

441

* Returns the line number at which the error occurred

442

* @return the line number (1-based) where the error was detected

443

*/

444

int getLine();

445

446

/**

447

* Returns the column number at which the error occurred

448

* @return the column number (1-based) where the error was detected

449

*/

450

int getColumn();

451

}

452

```

453

454

**Usage Examples:**

455

456

```java

457

import org.hjson.JsonValue;

458

import org.hjson.ParseException;

459

460

// Parse error handling with detailed information

461

public class ParsingErrorHandler {

462

public static JsonValue safeParse(String text) {

463

try {

464

return JsonValue.readHjson(text);

465

} catch (ParseException e) {

466

System.err.printf("Parse error at line %d, column %d (offset %d): %s%n",

467

e.getLine(), e.getColumn(), e.getOffset(), e.getMessage());

468

469

// Show context around error location

470

showErrorContext(text, e.getOffset());

471

472

return null;

473

}

474

}

475

476

private static void showErrorContext(String text, int offset) {

477

int contextStart = Math.max(0, offset - 20);

478

int contextEnd = Math.min(text.length(), offset + 20);

479

480

String context = text.substring(contextStart, contextEnd);

481

String pointer = " ".repeat(offset - contextStart) + "^";

482

483

System.err.println("Context:");

484

System.err.println(context);

485

System.err.println(pointer);

486

}

487

}

488

489

// Common parsing errors and their handling

490

public void demonstrateParsingErrors() {

491

String[] invalidInputs = {

492

"{ name: unclosed", // Missing closing brace

493

"{ \"key\": invalid_value }", // Invalid unquoted value

494

"{ key: \"unclosed string }", // Unclosed string

495

"[1, 2, 3,]", // Trailing comma (in JSON mode)

496

"{ duplicate: 1, duplicate: 2 }" // Duplicate keys (warning)

497

};

498

499

for (String input : invalidInputs) {

500

System.out.println("Parsing: " + input);

501

try {

502

JsonValue result = JsonValue.readJSON(input); // Strict JSON parsing

503

System.out.println("Success: " + result);

504

} catch (ParseException e) {

505

System.err.printf("Error at %d:%d - %s%n",

506

e.getLine(), e.getColumn(), e.getMessage());

507

}

508

System.out.println();

509

}

510

}

511

```

512

513

### Error Recovery Strategies

514

515

```java

516

// Robust parsing with fallback strategies

517

public class RobustParser {

518

public static JsonValue parseWithFallback(String text) {

519

// Try Hjson first (more lenient)

520

try {

521

return JsonValue.readHjson(text);

522

} catch (ParseException hjsonError) {

523

System.out.println("Hjson parse failed, trying JSON...");

524

525

// Fallback to strict JSON

526

try {

527

return JsonValue.readJSON(text);

528

} catch (ParseException jsonError) {

529

// Both failed, try to fix common issues

530

String fixedText = attemptAutoFix(text);

531

if (!fixedText.equals(text)) {

532

try {

533

return JsonValue.readJSON(fixedText);

534

} catch (ParseException finalError) {

535

throw new RuntimeException("Unable to parse after auto-fix attempts", finalError);

536

}

537

}

538

539

// Give up with detailed error

540

throw new RuntimeException("Parse failed in both Hjson and JSON modes", jsonError);

541

}

542

}

543

}

544

545

private static String attemptAutoFix(String text) {

546

// Common fixes

547

String fixed = text

548

.replaceAll(",\\s*([}\\]])", "$1") // Remove trailing commas

549

.replaceAll("'", "\"") // Replace single quotes

550

.trim();

551

552

// Ensure proper bracing for objects

553

if (!fixed.startsWith("{") && !fixed.startsWith("[")) {

554

fixed = "{ " + fixed + " }";

555

}

556

557

return fixed;

558

}

559

}

560

```

561

562

### Type Conversion Error Handling

563

564

```java

565

// Handle type conversion errors

566

public class TypeSafeAccessor {

567

public static void processValue(JsonValue value) {

568

try {

569

if (value.isNumber()) {

570

// Handle potential overflow

571

try {

572

int intValue = value.asInt();

573

System.out.println("Integer: " + intValue);

574

} catch (NumberFormatException e) {

575

// Value too large for int, try long

576

try {

577

long longValue = value.asLong();

578

System.out.println("Long: " + longValue);

579

} catch (NumberFormatException e2) {

580

// Use double as fallback

581

double doubleValue = value.asDouble();

582

System.out.println("Double: " + doubleValue);

583

}

584

}

585

} else if (value.isString()) {

586

String stringValue = value.asString();

587

System.out.println("String: " + stringValue);

588

} else {

589

System.out.println("Other type: " + value.getType());

590

}

591

} catch (UnsupportedOperationException e) {

592

System.err.println("Unexpected type conversion error: " + e.getMessage());

593

}

594

}

595

}

596

```

597

598

## Validation Patterns

599

600

### Schema Validation

601

602

```java

603

// Simple schema validation using type checking

604

public class JsonValidator {

605

public static class ValidationResult {

606

private final boolean valid;

607

private final List<String> errors;

608

609

public ValidationResult(boolean valid, List<String> errors) {

610

this.valid = valid;

611

this.errors = errors;

612

}

613

614

public boolean isValid() { return valid; }

615

public List<String> getErrors() { return errors; }

616

}

617

618

public static ValidationResult validateUserObject(JsonValue value) {

619

List<String> errors = new ArrayList<>();

620

621

if (!value.isObject()) {

622

errors.add("Root value must be an object");

623

return new ValidationResult(false, errors);

624

}

625

626

JsonObject obj = value.asObject();

627

628

// Required string field

629

JsonValue nameValue = obj.get("name");

630

if (nameValue == null) {

631

errors.add("Missing required field: name");

632

} else if (!nameValue.isString()) {

633

errors.add("Field 'name' must be a string");

634

} else if (nameValue.asString().trim().isEmpty()) {

635

errors.add("Field 'name' cannot be empty");

636

}

637

638

// Required number field

639

JsonValue ageValue = obj.get("age");

640

if (ageValue == null) {

641

errors.add("Missing required field: age");

642

} else if (!ageValue.isNumber()) {

643

errors.add("Field 'age' must be a number");

644

} else {

645

try {

646

int age = ageValue.asInt();

647

if (age < 0 || age > 150) {

648

errors.add("Field 'age' must be between 0 and 150");

649

}

650

} catch (NumberFormatException e) {

651

errors.add("Field 'age' must be a valid integer");

652

}

653

}

654

655

// Optional boolean field

656

JsonValue activeValue = obj.get("active");

657

if (activeValue != null && !activeValue.isBoolean()) {

658

errors.add("Field 'active' must be a boolean if present");

659

}

660

661

return new ValidationResult(errors.isEmpty(), errors);

662

}

663

}

664

665

// Usage

666

JsonValue userData = JsonValue.readHjson("""

667

{

668

name: "John Doe"

669

age: 30

670

active: true

671

}

672

""");

673

674

ValidationResult result = JsonValidator.validateUserObject(userData);

675

if (result.isValid()) {

676

System.out.println("Validation passed");

677

} else {

678

System.out.println("Validation errors:");

679

result.getErrors().forEach(System.out::println);

680

}

681

```

682

683

## Best Practices

684

685

### Type Safety Guidelines

686

687

1. **Always check types before conversion**: Use `is*()` methods before `as*()` methods

688

2. **Handle conversion exceptions**: Wrap type conversions in try-catch blocks when input is untrusted

689

3. **Use appropriate numeric types**: Choose int/long/double based on expected value ranges

690

4. **Provide default values**: Use safe accessor patterns for optional values

691

5. **Validate input early**: Perform type validation as close to input as possible

692

693

### Error Handling Best Practices

694

695

1. **Provide context**: Include line/column information in error messages

696

2. **Log parsing errors**: Always log ParseException details for debugging

697

3. **Graceful degradation**: Provide fallback parsing strategies when possible

698

4. **User-friendly messages**: Convert technical errors to user-friendly messages

699

5. **Early validation**: Validate structure and types immediately after parsing

700

701

### Performance Considerations

702

703

1. **Type checking cost**: Type checking is fast, conversion may involve validation

704

2. **Exception handling**: Avoid using exceptions for control flow

705

3. **Caching**: Cache type information for frequently accessed values

706

4. **Batch validation**: Validate entire structures in one pass when possible