CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-hjson--hjson

Hjson (Human JSON) configuration file format library for Java providing parsing and generation capabilities with human-friendly features.

Pending
Overview
Eval results
Files

types-exceptions.mddocs/

Type System and Error Handling

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

Capabilities

Type Checking

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

/**
 * Returns true if this is a JSON object
 * @return true if this value represents a JSON object
 */
boolean isObject();

/**
 * Returns true if this is a JSON array
 * @return true if this value represents a JSON array
 */
boolean isArray();

/**
 * Returns true if this is a JSON string
 * @return true if this value represents a JSON string
 */
boolean isString();

/**
 * Returns true if this is a JSON number
 * @return true if this value represents a JSON number
 */
boolean isNumber();

/**
 * Returns true if this is a JSON boolean
 * @return true if this value represents a JSON boolean
 */
boolean isBoolean();

/**
 * Returns true if this is the JSON literal true
 * @return true if this value represents the JSON literal true
 */
boolean isTrue();

/**
 * Returns true if this is the JSON literal false
 * @return true if this value represents the JSON literal false
 */
boolean isFalse();

/**
 * Returns true if this is the JSON literal null
 * @return true if this value represents the JSON literal null
 */
boolean isNull();

/**
 * Returns the type of this JSON value
 * @return JsonType enum representing the value type
 */
JsonType getType();

Usage Examples:

import org.hjson.JsonValue;
import org.hjson.JsonType;

JsonValue value = JsonValue.readHjson("""
    {
        name: "John Doe"
        age: 30
        active: true
        spouse: null
        children: []
        metadata: {}
    }
    """).asObject();

// Type checking for safe processing
for (JsonObject.Member member : value.asObject()) {
    String name = member.getName();
    JsonValue memberValue = member.getValue();
    
    if (memberValue.isString()) {
        System.out.println(name + " is a string: " + memberValue.asString());
    } else if (memberValue.isNumber()) {
        System.out.println(name + " is a number: " + memberValue.asInt());
    } else if (memberValue.isBoolean()) {
        System.out.println(name + " is a boolean: " + memberValue.asBoolean());
    } else if (memberValue.isNull()) {
        System.out.println(name + " is null");
    } else if (memberValue.isArray()) {
        System.out.println(name + " is an array with " + memberValue.asArray().size() + " elements");
    } else if (memberValue.isObject()) {
        System.out.println(name + " is an object with " + memberValue.asObject().size() + " members");
    }
    
    // Using getType() enum
    JsonType type = memberValue.getType();
    System.out.println(name + " type: " + type);
}

// Specific boolean value checking
JsonValue boolValue = JsonValue.valueOf(true);
if (boolValue.isTrue()) {
    System.out.println("Value is specifically true");
}

JsonValue falseValue = JsonValue.valueOf(false);
if (falseValue.isFalse()) {
    System.out.println("Value is specifically false");
}

Type Conversion

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

/**
 * Returns this JSON value as JsonObject
 * @return this value as JsonObject
 * @throws UnsupportedOperationException if this value is not a JSON object
 */
JsonObject asObject();

/**
 * Returns this JSON value as JsonArray
 * @return this value as JsonArray
 * @throws UnsupportedOperationException if this value is not a JSON array
 */
JsonArray asArray();

/**
 * Returns this JSON value as String
 * @return string representation of this value
 * @throws UnsupportedOperationException if this value is not a JSON string
 */
String asString();

/**
 * Returns this JSON value as int
 * @return int representation of this value
 * @throws UnsupportedOperationException if this value is not a JSON number
 * @throws NumberFormatException if the value cannot be represented as int
 */
int asInt();

/**
 * Returns this JSON value as long
 * @return long representation of this value
 * @throws UnsupportedOperationException if this value is not a JSON number
 * @throws NumberFormatException if the value cannot be represented as long
 */
long asLong();

/**
 * Returns this JSON value as float
 * @return float representation of this value
 * @throws UnsupportedOperationException if this value is not a JSON number
 * @throws NumberFormatException if the value cannot be represented as float
 */
float asFloat();

/**
 * Returns this JSON value as double
 * @return double representation of this value
 * @throws UnsupportedOperationException if this value is not a JSON number
 * @throws NumberFormatException if the value cannot be represented as double
 */
double asDouble();

/**
 * Returns this JSON value as boolean
 * @return boolean representation of this value
 * @throws UnsupportedOperationException if this value is not a JSON boolean
 */
boolean asBoolean();

/**
 * Returns this JSON value as DSF (Domain Specific Format) object
 * @return DSF object representation of this value
 * @throws UnsupportedOperationException if this value is not a DSF value
 */
Object asDsf();

Usage Examples:

import org.hjson.JsonValue;
import org.hjson.JsonObject;
import org.hjson.JsonArray;

// Safe type conversion with checking
JsonValue data = JsonValue.readHjson("""
    {
        config: {
            port: 8080
            name: "My App"
            enabled: true
        }
        servers: ["server1", "server2", "server3"]
        version: 1.5
    }
    """);

JsonObject root = data.asObject();

// Safe object conversion
JsonValue configValue = root.get("config");
if (configValue.isObject()) {
    JsonObject config = configValue.asObject();
    
    // Safe numeric conversion
    JsonValue portValue = config.get("port");
    if (portValue.isNumber()) {
        int port = portValue.asInt();
        System.out.println("Port: " + port);
    }
    
    // Safe string conversion
    JsonValue nameValue = config.get("name");
    if (nameValue.isString()) {
        String name = nameValue.asString();
        System.out.println("Name: " + name);
    }
    
    // Safe boolean conversion
    JsonValue enabledValue = config.get("enabled");
    if (enabledValue.isBoolean()) {
        boolean enabled = enabledValue.asBoolean();
        System.out.println("Enabled: " + enabled);
    }
}

// Safe array conversion
JsonValue serversValue = root.get("servers");
if (serversValue.isArray()) {
    JsonArray servers = serversValue.asArray();
    System.out.println("Server count: " + servers.size());
    
    for (JsonValue serverValue : servers) {
        if (serverValue.isString()) {
            System.out.println("Server: " + serverValue.asString());
        }
    }
}

// Numeric precision handling
JsonValue versionValue = root.get("version");
if (versionValue.isNumber()) {
    double version = versionValue.asDouble();
    float versionFloat = versionValue.asFloat();
    System.out.println("Version (double): " + version);
    System.out.println("Version (float): " + versionFloat);
}

Error-Safe Type Conversion

Handle type conversion errors gracefully with try-catch patterns.

// Error-safe conversion patterns
public class SafeConverter {
    public static String safeAsString(JsonValue value, String defaultValue) {
        try {
            return value.isString() ? value.asString() : defaultValue;
        } catch (UnsupportedOperationException e) {
            return defaultValue;
        }
    }
    
    public static int safeAsInt(JsonValue value, int defaultValue) {
        try {
            return value.isNumber() ? value.asInt() : defaultValue;
        } catch (UnsupportedOperationException | NumberFormatException e) {
            return defaultValue;
        }
    }
    
    public static boolean safeAsBoolean(JsonValue value, boolean defaultValue) {
        try {
            return value.isBoolean() ? value.asBoolean() : defaultValue;
        } catch (UnsupportedOperationException e) {
            return defaultValue;
        }
    }
    
    public static JsonObject safeAsObject(JsonValue value) {
        try {
            return value.isObject() ? value.asObject() : null;
        } catch (UnsupportedOperationException e) {
            return null;
        }
    }
}

// Usage
JsonValue unknownValue = getValue(); // Could be any type
String stringValue = SafeConverter.safeAsString(unknownValue, "default");
int intValue = SafeConverter.safeAsInt(unknownValue, 0);
JsonObject objValue = SafeConverter.safeAsObject(unknownValue);
if (objValue != null) {
    // Process object safely
}

JsonType Enumeration

Enumeration of all possible JSON value types.

/**
 * Enumeration of JSON value types
 */
enum JsonType {
    /**
     * JSON string value
     */
    STRING,
    
    /**
     * JSON number value
     */
    NUMBER,
    
    /**
     * JSON object value
     */
    OBJECT,
    
    /**
     * JSON array value
     */
    ARRAY,
    
    /**
     * JSON boolean value (true or false)
     */
    BOOLEAN,
    
    /**
     * JSON null value
     */
    NULL,
    
    /**
     * Domain Specific Format value (specialized parsing)
     */
    DSF
}

Usage Examples:

import org.hjson.JsonType;
import org.hjson.JsonValue;

JsonValue mixed = JsonValue.readHjson("""
    [
        "text",
        42,
        true,
        null,
        { key: "value" },
        [1, 2, 3]
    ]
    """).asArray();

// Process elements based on type
for (JsonValue element : mixed.asArray()) {
    JsonType type = element.getType();
    
    switch (type) {
        case STRING:
            System.out.println("String: " + element.asString());
            break;
        case NUMBER:
            System.out.println("Number: " + element.asDouble());
            break;
        case BOOLEAN:
            System.out.println("Boolean: " + element.asBoolean());
            break;
        case NULL:
            System.out.println("Null value");
            break;
        case OBJECT:
            System.out.println("Object with " + element.asObject().size() + " members");
            break;
        case ARRAY:
            System.out.println("Array with " + element.asArray().size() + " elements");
            break;
        case DSF:
            System.out.println("DSF value: " + element.asDsf());
            break;
    }
}

// Type-based filtering
public static List<JsonValue> filterByType(JsonArray array, JsonType targetType) {
    List<JsonValue> filtered = new ArrayList<>();
    for (JsonValue element : array) {
        if (element.getType() == targetType) {
            filtered.add(element);
        }
    }
    return filtered;
}

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

Error Handling

ParseException

Exception thrown when parsing invalid JSON or Hjson input.

/**
 * Unchecked exception that indicates a problem while parsing JSON or Hjson text
 * Provides detailed information about the location and nature of the parsing error
 */
class ParseException extends RuntimeException {
    /**
     * Returns the character offset at which the error occurred
     * @return the character offset (0-based) where the error was detected
     */
    int getOffset();
    
    /**
     * Returns the line number at which the error occurred
     * @return the line number (1-based) where the error was detected
     */
    int getLine();
    
    /**
     * Returns the column number at which the error occurred
     * @return the column number (1-based) where the error was detected
     */
    int getColumn();
}

Usage Examples:

import org.hjson.JsonValue;
import org.hjson.ParseException;

// Parse error handling with detailed information
public class ParsingErrorHandler {
    public static JsonValue safeParse(String text) {
        try {
            return JsonValue.readHjson(text);
        } catch (ParseException e) {
            System.err.printf("Parse error at line %d, column %d (offset %d): %s%n",
                e.getLine(), e.getColumn(), e.getOffset(), e.getMessage());
            
            // Show context around error location
            showErrorContext(text, e.getOffset());
            
            return null;
        }
    }
    
    private static void showErrorContext(String text, int offset) {
        int contextStart = Math.max(0, offset - 20);
        int contextEnd = Math.min(text.length(), offset + 20);
        
        String context = text.substring(contextStart, contextEnd);
        String pointer = " ".repeat(offset - contextStart) + "^";
        
        System.err.println("Context:");
        System.err.println(context);
        System.err.println(pointer);
    }
}

// Common parsing errors and their handling
public void demonstrateParsingErrors() {
    String[] invalidInputs = {
        "{ name: unclosed",           // Missing closing brace
        "{ \"key\": invalid_value }", // Invalid unquoted value
        "{ key: \"unclosed string }", // Unclosed string
        "[1, 2, 3,]",                // Trailing comma (in JSON mode)
        "{ duplicate: 1, duplicate: 2 }" // Duplicate keys (warning)
    };
    
    for (String input : invalidInputs) {
        System.out.println("Parsing: " + input);
        try {
            JsonValue result = JsonValue.readJSON(input); // Strict JSON parsing
            System.out.println("Success: " + result);
        } catch (ParseException e) {
            System.err.printf("Error at %d:%d - %s%n", 
                e.getLine(), e.getColumn(), e.getMessage());
        }
        System.out.println();
    }
}

Error Recovery Strategies

// Robust parsing with fallback strategies
public class RobustParser {
    public static JsonValue parseWithFallback(String text) {
        // Try Hjson first (more lenient)
        try {
            return JsonValue.readHjson(text);
        } catch (ParseException hjsonError) {
            System.out.println("Hjson parse failed, trying JSON...");
            
            // Fallback to strict JSON
            try {
                return JsonValue.readJSON(text);
            } catch (ParseException jsonError) {
                // Both failed, try to fix common issues
                String fixedText = attemptAutoFix(text);
                if (!fixedText.equals(text)) {
                    try {
                        return JsonValue.readJSON(fixedText);
                    } catch (ParseException finalError) {
                        throw new RuntimeException("Unable to parse after auto-fix attempts", finalError);
                    }
                }
                
                // Give up with detailed error
                throw new RuntimeException("Parse failed in both Hjson and JSON modes", jsonError);
            }
        }
    }
    
    private static String attemptAutoFix(String text) {
        // Common fixes
        String fixed = text
            .replaceAll(",\\s*([}\\]])", "$1")  // Remove trailing commas
            .replaceAll("'", "\"")              // Replace single quotes
            .trim();
        
        // Ensure proper bracing for objects
        if (!fixed.startsWith("{") && !fixed.startsWith("[")) {
            fixed = "{ " + fixed + " }";
        }
        
        return fixed;
    }
}

Type Conversion Error Handling

// Handle type conversion errors
public class TypeSafeAccessor {
    public static void processValue(JsonValue value) {
        try {
            if (value.isNumber()) {
                // Handle potential overflow
                try {
                    int intValue = value.asInt();
                    System.out.println("Integer: " + intValue);
                } catch (NumberFormatException e) {
                    // Value too large for int, try long
                    try {
                        long longValue = value.asLong();
                        System.out.println("Long: " + longValue);
                    } catch (NumberFormatException e2) {
                        // Use double as fallback
                        double doubleValue = value.asDouble();
                        System.out.println("Double: " + doubleValue);
                    }
                }
            } else if (value.isString()) {
                String stringValue = value.asString();
                System.out.println("String: " + stringValue);
            } else {
                System.out.println("Other type: " + value.getType());
            }
        } catch (UnsupportedOperationException e) {
            System.err.println("Unexpected type conversion error: " + e.getMessage());
        }
    }
}

Validation Patterns

Schema Validation

// Simple schema validation using type checking
public class JsonValidator {
    public static class ValidationResult {
        private final boolean valid;
        private final List<String> errors;
        
        public ValidationResult(boolean valid, List<String> errors) {
            this.valid = valid;
            this.errors = errors;
        }
        
        public boolean isValid() { return valid; }
        public List<String> getErrors() { return errors; }
    }
    
    public static ValidationResult validateUserObject(JsonValue value) {
        List<String> errors = new ArrayList<>();
        
        if (!value.isObject()) {
            errors.add("Root value must be an object");
            return new ValidationResult(false, errors);
        }
        
        JsonObject obj = value.asObject();
        
        // Required string field
        JsonValue nameValue = obj.get("name");
        if (nameValue == null) {
            errors.add("Missing required field: name");
        } else if (!nameValue.isString()) {
            errors.add("Field 'name' must be a string");
        } else if (nameValue.asString().trim().isEmpty()) {
            errors.add("Field 'name' cannot be empty");
        }
        
        // Required number field
        JsonValue ageValue = obj.get("age");
        if (ageValue == null) {
            errors.add("Missing required field: age");
        } else if (!ageValue.isNumber()) {
            errors.add("Field 'age' must be a number");
        } else {
            try {
                int age = ageValue.asInt();
                if (age < 0 || age > 150) {
                    errors.add("Field 'age' must be between 0 and 150");
                }
            } catch (NumberFormatException e) {
                errors.add("Field 'age' must be a valid integer");
            }
        }
        
        // Optional boolean field
        JsonValue activeValue = obj.get("active");
        if (activeValue != null && !activeValue.isBoolean()) {
            errors.add("Field 'active' must be a boolean if present");
        }
        
        return new ValidationResult(errors.isEmpty(), errors);
    }
}

// Usage
JsonValue userData = JsonValue.readHjson("""
    {
        name: "John Doe"
        age: 30
        active: true
    }
    """);

ValidationResult result = JsonValidator.validateUserObject(userData);
if (result.isValid()) {
    System.out.println("Validation passed");
} else {
    System.out.println("Validation errors:");
    result.getErrors().forEach(System.out::println);
}

Best Practices

Type Safety Guidelines

  1. Always check types before conversion: Use is*() methods before as*() methods
  2. Handle conversion exceptions: Wrap type conversions in try-catch blocks when input is untrusted
  3. Use appropriate numeric types: Choose int/long/double based on expected value ranges
  4. Provide default values: Use safe accessor patterns for optional values
  5. Validate input early: Perform type validation as close to input as possible

Error Handling Best Practices

  1. Provide context: Include line/column information in error messages
  2. Log parsing errors: Always log ParseException details for debugging
  3. Graceful degradation: Provide fallback parsing strategies when possible
  4. User-friendly messages: Convert technical errors to user-friendly messages
  5. Early validation: Validate structure and types immediately after parsing

Performance Considerations

  1. Type checking cost: Type checking is fast, conversion may involve validation
  2. Exception handling: Avoid using exceptions for control flow
  3. Caching: Cache type information for frequently accessed values
  4. Batch validation: Validate entire structures in one pass when possible

Install with Tessl CLI

npx tessl i tessl/maven-org-hjson--hjson

docs

configuration.md

index.md

json-arrays.md

json-objects.md

parsing.md

serialization.md

types-exceptions.md

tile.json