Need help with your JSON?

Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool

Command-Line Interface Design for JSON Formatters

JSON (JavaScript Object Notation) is the de facto standard for data exchange on the web and in many other contexts. While highly flexible, JSON can sometimes be hard to read, especially when it's minified or deeply nested. Command-line interface (CLI) tools that format (or "pretty-print") JSON are essential utilities for developers. This article explores common patterns and best practices for designing intuitive and powerful CLIs for JSON formatters.

Core Principles: Standard I/O and Files

A robust CLI formatter should support reading JSON from multiple sources and writing to multiple destinations. The most fundamental are standard input (stdin) and standard output (stdout), enabling pipeline usage, and direct file input/output.

Standard Input and Output (Piping)

This is arguably the most common use case. A user pipes JSON data from another command into your formatter, and the formatted output is printed to the console or piped to another command. This follows the Unix philosophy of small tools doing one thing well and connecting via pipes.

echo '<!-- Use HTML entity for quote --'{"name":"Alice"}' | your_formatter

cat data.json | your_formatter

curl api.com/data | your_formatter | less

Your formatter should read from stdin if no input file is specified. The formatted output should go to stdout.

File Input and Output

Allowing users to specify input and output files is also crucial.

your_formatter input.json

your_formatter input.json > output.json

your_formatter input.json --output output.json

If an input file is given but no output destination (either stdout implied or explicit output file), write to stdout. If an output file is specified, write to that file.

Avoid In-Place Modification by Default:

While an option for in-place modification ('--write' or '--inplace') can be convenient, it should never be the default behavior as it can lead to accidental data loss or corruption if the formatting fails. Require an explicit flag for this.

Command-Line Options and Flags

Options control how the formatting is performed. Common options for JSON formatters include:

Indentation

This is the most fundamental option. Users should be able to specify the number of spaces or use tabs for indentation.

your_formatter input.json --indent 2 // 2 spaces

your_formatter input.json --indent 4 // 4 spaces (often default)

your_formatter input.json --indent "\\t" // Tab indentation

your_formatter input.json --indent 0 // No indentation (compact output)

A common default is 2 or 4 spaces. 0 or no indent option implies compact output.

Compact Output

A flag to explicitly request the most compact output (no whitespace between tokens, except where required). This is equivalent to '--indent 0'.

your_formatter input.json --compact

Sort Keys

An option to sort keys within JSON objects alphabetically. This helps in comparing different versions of JSON data.

your_formatter input.json --sort-keys

Validation / Check Only

A mode to simply validate if the input is well-formed JSON, without printing any formatted output. This is useful in scripts or CI pipelines. The tool should exit with a status code of 0 for success and a non-zero code for failure.

your_formatter input.json --check

if your_formatter input.json --check; then

echo "Valid JSON";

exit 0;

else

echo "Invalid JSON";

exit 1;

fi

Help and Version Information

Standard flags like '--help' (or '-h') and '--version' (or '-v') should provide usage instructions and the tool's version number, respectively.

Implementation Considerations

Implementing a JSON formatter CLI involves:

  • Argument Parsing: Use a library or manually parse 'process.argv' to understand user options and input/output paths.
  • Reading Input:If a file is specified, read its content using file system APIs. If no file and stdin is potentially available (e.g., not a TTY), read from 'process.stdin'. Be mindful of large inputs; streaming might be necessary for very big files, though simple formatters often load everything into memory.
  • Parsing JSON: Use the built-in 'JSON.parse()' method. Handle potential 'SyntaxError' exceptions.
  • Formatting JSON: Use 'JSON.stringify()' with its 'space' argument for indentation. Sorting keys requires iterating over object keys, sorting them, and then rebuilding the object or stringifying with a custom replacer function.
  • Writing Output:If an output file is specified, write the formatted string using file system APIs. Otherwise, write to 'process.stdout'.
  • Error Handling: Catch parsing errors, file I/O errors, and invalid option usage. Print informative error messages to 'process.stderr' and exit with a non-zero status code.

A simple example of parsing and formatting in JavaScript/TypeScript:

Basic Formatting Logic (Conceptual):

function formatJson(jsonString: string, indent: string | number = 2, sortKeys: boolean = false): string {
  try {
    let parsed = JSON.parse(jsonString);

    if (sortKeys) {
      // Recursive function to sort keys in objects
      const sortObjectKeys = (obj: any): any => {
        if (Array.isArray(obj)) {
          return obj.map(sortObjectKeys);
        } else if (typeof obj === 'object' && obj !== null) {
          const sortedKeys = Object.keys(obj).sort();
          const sortedObj: any = {};
          for (const key of sortedKeys) {
            sortedObj[key] = sortObjectKeys(obj[key]);
          }
          return sortedObj;
        }
        return obj;
      };
      parsed = sortObjectKeys(parsed);
    }

    // Use JSON.stringify for pretty-printing
    return JSON.stringify(parsed, null, indent);

  } catch (err: unknown) {
    if (err instanceof SyntaxError) {
      // err is narrowed to SyntaxError here, safe to access message
      // FIX: Replace template literal inside string with concatenation to avoid build issue
      throw new Error('Invalid JSON: ' + err.message); // Extract message and use directly
    }
    // Re-throw other errors (err is unknown here)
    throw err;
  }
}

// Example usage within a theoretical CLI script:
// const inputJson = readInput(); // Function to read from stdin or file
// try {
//   const formattedJson = formatJson(inputJson, 4, true);
//   writeOutput(formattedJson); // Function to write to stdout or file
// } catch (err: unknown) {
//   // Need to check type if accessing properties, but console.error accepts unknown
//   console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
//   process.exit(1); // Indicate failure
// }

Best Practices and User Experience

  • Clear Documentation: Provide comprehensive help text ('--help') and README documentation.
  • Consistent Options: Use standard conventions for option names (e.g., '--input', '--output', '--indent').
  • Sensible Defaults: Choose reasonable defaults (e.g., 2 or 4 space indentation) when options are not specified.
  • Never Fail Silently: Always report errors clearly to the user via stderr and exit codes.
  • Handle Edge Cases: Consider empty objects/arrays, large numbers, specific string escapes, etc.
  • Performance: For very large files, streaming parsers/formatters might be necessary, although this adds significant complexity.

Conclusion

Designing a good CLI for a JSON formatter involves more than just writing the formatting logic. It requires careful consideration of how users will interact with the tool in different scenarios — from simple manual formatting to integration into automated scripts. By adhering to standard CLI patterns like supporting stdin/stdout, providing clear options for formatting style, and implementing robust error handling, you can create a useful and developer-friendly utility.

Need help with your JSON?

Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool