JSON and YAML Processing
Utah provides comprehensive JSON and YAML processing capabilities that allow you to work with structured data directly iParse YAML content into an object:
// Read YAML from a file
let yamlConfig: string = fs.readFile("app-config.yaml");
let config: object = yaml.parse(yamlConfig);
console.log("YAML configuration loaded");
``` These functions use industry-standard tools like `jq` and `yq` under the hood.
## JSON Functions
### Installation and Setup
Before using JSON functions, ensure dependencies are available:
```typescript
// Automatically install jq if not available
json.installDependencies();
// Check if jq is installed manually
if (!os.isInstalled("jq")) {
console.log("jq is required for JSON processing");
exit(1);
}
JSON Parsing and Validation
json.isValid(jsonString): boolean
Check if a string contains valid JSON syntax:
let data: string = "{\"name\": \"Utah\", \"version\": 1.0}";
let valid: boolean = json.isValid(data);
if (valid) {
console.log("JSON is valid");
} else {
console.log("Invalid JSON format");
exit(1);
}
json.parse(jsonString): object
Parse a JSON string into an object:
let jsonData: string = "{\"app\": {\"name\": \"MyApp\", \"port\": 8080}}";
let config: object = json.parse(jsonData);
console.log("Configuration loaded");
json.stringify(jsonObject): string
Convert a JSON object back to a string:
let configString: string = json.stringify(config);
console.log("Config as string: " + configString);
JSON Property Access
json.get(jsonObject, path): string
Get a value using jq-style path syntax:
let userData: string = "{\"user\": {\"name\": \"Alice\", \"settings\": {\"theme\": \"dark\"}}}";
let user: object = json.parse(userData);
// Access nested properties
let userName: string = json.get(user, ".user.name"); // "Alice"
let theme: string = json.get(user, ".user.settings.theme"); // "dark"
// Access array elements
let tagsJson: string = "{\"tags\": [\"work\", \"personal\", \"urgent\"]}";
let tags: object = json.parse(tagsJson);
let firstTag: string = json.get(tags, ".tags[0]"); // "work"
let lastTag: string = json.get(tags, ".tags[-1]"); // "urgent"
json.set(jsonObject, path, value): object
Set a value at the specified path:
let config: object = json.parse('{"database": {"host": "localhost"}}');
// Set simple values
config = json.set(config, ".database.port", 5432);
config = json.set(config, ".database.ssl", true);
// Set nested objects
config = json.set(config, ".auth.enabled", true);
config = json.set(config, ".auth.method", "oauth");
// Set array elements
config = json.set(config, ".features[0]", "logging");
config = json.set(config, ".features[1]", "monitoring");
json.has(jsonObject, path): boolean
Check if a property exists at the given path:
let data: object = json.parse('{"user": {"name": "Alice", "email": null}}');
let hasName: boolean = json.has(data, ".user.name"); // true
let hasEmail: boolean = json.has(data, ".user.email"); // true (exists but null)
let hasPhone: boolean = json.has(data, ".user.phone"); // false
json.delete(jsonObject, path): object
Remove a property from the JSON object:
let user: object = json.parse('{"name": "Alice", "temp": "remove", "age": 30}');
// Remove unwanted property
user = json.delete(user, ".temp");
// Remove nested property
user = json.delete(user, ".profile.avatar");
JSON Utility Functions
json.keys(jsonObject): string[]
Get all keys from a JSON object:
let config: object = json.parse('{"host": "localhost", "port": 8080, "ssl": true}');
let keys: string[] = json.keys(config);
for (let key: string in keys) {
console.log("Config key: " + key);
}
// Output: host, port, ssl
json.values(jsonObject): any[]
Get all values from a JSON object:
let settings: object = json.parse('{"theme": "dark", "lang": "en", "debug": true}');
let values: any[] = json.values(settings);
for (let value: any in values) {
console.log("Setting value: " + value);
}
// Output: dark, en, true
json.merge(jsonObject1, jsonObject2): object
Merge two JSON objects (second object takes precedence):
let defaults: string = "{\"timeout\": 30, \"retries\": 3, \"debug\": false}";
let userConfig: string = "{\"timeout\": 60, \"debug\": true, \"verbose\": true}";
let defaultObj: object = json.parse(defaults);
let userObj: object = json.parse(userConfig);
let finalConfig: object = json.merge(defaultObj, userObj);
// Result: {"timeout": 60, "retries": 3, "debug": true, "verbose": true}
YAML Functions
YAML functions require both yq
and jq
:
// Automatically install yq and jq if not available
yaml.installDependencies();
// Manual dependency check
if (!os.isInstalled("yq") || !os.isInstalled("jq")) {
console.log("YAML functions require both yq and jq");
exit(1);
}
YAML Parsing and Validation
yaml.isValid(yamlString): boolean
Check if a string contains valid YAML syntax:
// Read YAML from a file instead of multiline strings
let configYaml: string = fs.readFile("config.yaml");
let valid: boolean = yaml.isValid(configYaml);
if (valid) {
console.log("YAML is valid");
}
yaml.parse(yamlString): object
Parse YAML string into an object:
let yamlConfig: string = `
app:
name: MyApp
version: "1.0"
settings:
debug: true
theme: dark
`;
let config: object = yaml.parse(yamlConfig);
console.log("YAML configuration loaded");
yaml.stringify(yamlObject): string
Convert object back to YAML format:
let yamlString: string = yaml.stringify(config);
console.log("YAML output:\n" + yamlString);
YAML Property Access
YAML functions use the same jq-style path syntax as JSON functions:
// Read Kubernetes manifest from file
let k8sManifest: string = fs.readFile("deployment.yaml");
`;
let deployment: object = yaml.parse(k8sManifest);
// Access nested properties
let appName: string = yaml.get(deployment, ".metadata.name");
let replicas: number = yaml.get(deployment, ".spec.replicas");
let image: string = yaml.get(deployment, ".spec.template.spec.containers[0].image");
console.log("Deploying ${appName} with ${replicas} replicas using ${image}");
// Update deployment
deployment = yaml.set(deployment, ".spec.replicas", 5);
deployment = yaml.set(deployment, ".spec.template.spec.containers[0].image", "myapp:v2.0");
// Save updated manifest
let updatedYaml: string = yaml.stringify(deployment);
fs.writeFile("deployment-updated.yaml", updatedYaml);
Practical Examples
Configuration Management
script.description("Configuration management script");
// Load and merge configuration files
let defaultConfigPath: string = "config/defaults.json";
let userConfigPath: string = "config/user.json";
if (!fs.exists(defaultConfigPath)) {
console.log("Default configuration not found");
exit(1);
}
let defaultConfig: string = fs.readFile(defaultConfigPath);
let finalConfig: object = json.parse(defaultConfig);
// Merge user configuration if exists
if (fs.exists(userConfigPath)) {
let userConfig: string = fs.readFile(userConfigPath);
let userObj: object = json.parse(userConfig);
finalConfig = json.merge(finalConfig, userObj);
console.log("Merged user configuration");
}
// Extract important settings
let dbHost: string = json.get(finalConfig, ".database.host");
let dbPort: number = json.get(finalConfig, ".database.port");
let debugMode: boolean = json.get(finalConfig, ".debug");
console.log("Database: ${dbHost}:${dbPort}");
console.log("Debug mode: ${debugMode}");
// Save final configuration
let configOutput: string = json.stringify(finalConfig);
fs.writeFile("config/final.json", configOutput);
Kubernetes Deployment Automation
script.description("Kubernetes deployment updater");
// Define command line arguments
args.define("--image", "-i", "Container image tag", "string", true);
args.define("--replicas", "-r", "Number of replicas", "number", false, 3);
args.define("--manifest", "-f", "Deployment manifest file", "string", false, "deployment.yaml");
let imageTag: string = args.get("--image");
let replicas: number = args.get("--replicas");
let manifestFile: string = args.get("--manifest");
if (!fs.exists(manifestFile)) {
console.log("Manifest file not found: ${manifestFile}");
exit(1);
}
// Load and parse Kubernetes manifest
let manifestContent: string = fs.readFile(manifestFile);
let deployment: object = yaml.parse(manifestContent);
// Update deployment configuration
deployment = yaml.set(deployment, ".spec.replicas", replicas);
deployment = yaml.set(deployment, ".spec.template.spec.containers[0].image", imageTag);
// Add deployment timestamp
let timestamp: string = timer.current().toString();
deployment = yaml.set(deployment, ".metadata.annotations.\"updated-at\"", timestamp);
// Save updated manifest
let updatedManifest: string = yaml.stringify(deployment);
let outputFile: string = manifestFile.replace(".yaml", "-updated.yaml");
fs.writeFile(outputFile, updatedManifest);
console.log("Updated manifest saved to: ${outputFile}");
console.log("Image: ${imageTag}");
console.log("Replicas: ${replicas}");
API Response Processing
script.description("Process API responses with JSON");
// Ensure JSON tools are available
json.installDependencies();
// Fetch data from API
let apiUrl: string = "https://api.github.com/repos/polatengin/utah";
let response: string = web.get(apiUrl);
if (!json.isValid(response)) {
console.log("Invalid JSON response from API");
exit(1);
}
let repoData: object = json.parse(response);
// Extract repository information
let repoName: string = json.get(repoData, ".name");
let description: string = json.get(repoData, ".description");
let stars: number = json.get(repoData, ".stargazers_count");
let language: string = json.get(repoData, ".language");
let lastUpdate: string = json.get(repoData, ".updated_at");
// Create summary object
let summary: object = json.parse("{}");
summary = json.set(summary, ".repository", repoName);
summary = json.set(summary, ".description", description);
summary = json.set(summary, ".stars", stars);
summary = json.set(summary, ".language", language);
summary = json.set(summary, ".last_updated", lastUpdate);
// Save summary
let summaryJson: string = json.stringify(summary);
fs.writeFile("repo-summary.json", summaryJson);
console.log("Repository: ${repoName}");
console.log("Stars: ${stars}");
console.log("Language: ${language}");
Error Handling
JSON Error Handling
try {
let data: string = fs.readFile("config.json");
if (!json.isValid(data)) {
console.log("Configuration file contains invalid JSON");
exit(1);
}
let config: object = json.parse(data);
let dbHost: string = json.get(config, ".database.host");
if (dbHost == "") {
console.log("Database host not configured");
exit(1);
}
console.log("Using database: ${dbHost}");
}
catch {
console.log("Failed to load configuration");
exit(1);
}
YAML Error Handling
try {
yaml.installDependencies();
let yamlContent: string = fs.readFile("docker-compose.yml");
if (!yaml.isValid(yamlContent)) {
console.log("Invalid YAML in docker-compose.yml");
exit(1);
}
let compose: object = yaml.parse(yamlContent);
// Check if required services exist
if (!yaml.has(compose, ".services.app")) {
console.log("App service not found in docker-compose.yml");
exit(1);
}
console.log("Docker Compose configuration is valid");
}
catch {
console.log("Failed to process YAML file");
exit(1);
}
Best Practices
1. Always Validate Input
// Good - validate before processing
if (json.isValid(jsonData)) {
let obj: object = json.parse(jsonData);
// Process safely
} else {
console.log("Invalid JSON input");
exit(1);
}
// Avoid - parsing without validation
let obj: object = json.parse(jsonData); // Might fail
2. Use Dependency Installation
// Good - ensure tools are available
json.installDependencies();
yaml.installDependencies();
// Then use functions safely
let data: object = json.parse(jsonString);
3. Handle Missing Properties Gracefully
// Good - check before accessing
if (json.has(config, ".database.host")) {
let host: string = json.get(config, ".database.host");
} else {
console.log("Database host not configured");
let host: string = "localhost"; // Use default
}
4. Use Meaningful Path Expressions
// Good - clear and specific
let userEmail: string = json.get(userData, ".user.profile.email");
let firstTag: string = json.get(tags, ".tags[0]");
// Less clear
let email: string = json.get(userData, ".user.profile.email");
5. Save Important Data
// Process and save results
let processedData: object = json.merge(defaults, userConfig);
let outputJson: string = json.stringify(processedData);
fs.writeFile("output/processed-config.json", outputJson);
Troubleshooting
Common Issues
-
Missing Dependencies:
// Solution: Use auto-install functions
json.installDependencies();
yaml.installDependencies(); -
Invalid JSON/YAML:
// Solution: Always validate first
if (!json.isValid(data)) {
console.log("Invalid format detected");
exit(1);
} -
Path Syntax Errors:
// Correct jq path syntax
json.get(obj, ".user.settings.theme"); // Object property
json.get(obj, ".items[0]"); // Array element
json.get(obj, ".tags[-1]"); // Last array element
Integration with Other Functions
JSON and YAML functions work seamlessly with other Utah features:
// With file system
let config: string = fs.readFile("app.json");
let configObj: object = json.parse(config);
// With web requests
let apiData: string = web.get("https://api.example.com/config");
let apiObj: object = json.parse(apiData);
// With environment variables
let dbHost: string = env.get("DB_HOST", "localhost");
configObj = json.set(configObj, ".database.host", dbHost);
// With parallel execution
parallel processConfigFile("config1.json");
parallel processConfigFile("config2.json");
parallel processConfigFile("config3.json");