rollpie get rust/simple_json_formatter@0.1.0
A Rust library and command that formats JSON. It has no dependencies and uses only std.
Numbers come out exactly as written. 1.50, 1E5 and integers with many digits all stay as they are. Object keys come out in the order they appear, unless sorting is asked for.
The Japanese documentation is in README.ja.md.
In the library, pass a string and Options to format. The result has no trailing newline.
use simple_json_formatter::{Indent, Options, format};
let options = Options {
indent: Indent::Tab,
..Options::default()
};
let text = format(r#"{"name":"日本語","tag":["a","b"]}"#, &options)?;
When no file is given, the command reads standard input and writes to standard output.
simple_json_formatter data.json format and write to standard output
cat data.json | simple_json_formatter -t format standard input with tabs
simple_json_formatter -w data/*.json rewrite the files in place
simple_json_formatter --check a.json only check whether it is formatted
The help and the error messages of the command are in Japanese.
The default indent is two spaces.
-i N sets the number of spaces between 0 and 64, and -t uses a tab.
-c drops all whitespace and puts everything on one line.
--max-width N puts an array or an object on one line when it fits in that many columns.
The count covers the whole line, including the indent, the key and the trailing comma.
CJK characters and emoji count as two columns, and a tab counts as four.
-s sorts object keys in dictionary order, down into nested objects.
-a turns every non-ASCII character into \uXXXX.
Characters outside the BMP become surrogate pairs.
-w overwrites each input file with the formatted result.
A file whose content does not change is not written.
It writes a temporary file in the same directory and then replaces the original, so a stop in the middle never breaks the original.
The permissions of the original are carried over, and for a symbolic link the target is rewritten.
For this reason, the directory that holds the file must be writable as well.
--check rewrites nothing and prints the names of the files that are not formatted.
The exit code is 0 on success, 1 when --check finds a file that is not formatted, and 2 on error.
Input:
{"name":"日本語","tag":["a","b","c"],"nested":{"z":1,"a":{"deep":[1,2,3]}}}
-t:
{
"name": "日本語",
"tag": [
"a",
"b",
"c"
],
"nested": {
"z": 1,
"a": {
"deep": [
1,
2,
3
]
}
}
}
--max-width 60:
{
"name": "日本語",
"tag": ["a", "b", "c"],
"nested": {"z": 1, "a": {"deep": [1, 2, 3]}}
}
-c -a:
{"name":"\u65e5\u672c\u8a9e","tag":["a","b","c"],"nested":{"z":1,"a":{"deep":[1,2,3]}}}
Only JSON as defined by RFC 8259 is accepted. Comments and trailing commas are errors. An error carries a line and a column. Columns count characters, not bytes.
simple_json_formatter: data.json: 2 行 8 列: tru は JSON の値ではありません
The message says that at line 2, column 8, tru is not a JSON value.
When an error message shows a character from the input, invisible characters and characters that change the direction of text are shown as a code point such as U+202E.
An unknown word is shown only up to its first 20 characters.
Both keep the input from disturbing the terminal.
Escapes in strings are decoded once and written again. \u3042 becomes あ, and \/ becomes /.
When the same key appears twice, both are kept.
A BOM at the start is removed when reading. Nesting is limited to 128 levels.
The speed was compared with serde_json 1.0.151 on three files that are often used to compare JSON speed. The time runs from receiving a string to returning the formatted string, measured on an Apple M4, and is the median. Reading and writing files is not included.
serde_json was measured in two ways: with no features, and with preserve_order and arbitrary_precision.
The latter keeps the order of keys and the way numbers are written, and it is the setting the prototype jsonfmt used.
Its output does not differ from this library by a single byte.
serde_json with no features sorts keys and rewrites numbers, so its output is not the same.
The times below are for this library, serde_json with no features and serde_json with both features, in that order.
With -c the order stays the same. For canada.json the times were 4.2ms, 6.1ms and 18.7ms.
To measure again, fetch the data with compare/fetch.py and run the two commands below.
The second one is serde_json with preserve_order and arbitrary_precision.
cargo run --release --manifest-path compare/bench/Cargo.toml -- \
compare/data/canada.json \
compare/data/citm_catalog.json \
compare/data/twitter.json
cargo run --release --manifest-path compare/bench/Cargo.toml --features exact -- \
compare/data/canada.json \
compare/data/citm_catalog.json \
compare/data/twitter.json
Formatting a 72MB file with the command took 0.7 seconds, including start-up, reading and writing.
Memory use is about 7 times the input for data that mixes many kinds of values.
For data made only of small values, such as [0,0,0,...], it is about 20 times.
The whole input is read into memory, so check its size first when handling large input you do not trust.
This library is based on a prototype called jsonfmt. The prototype used serde_json, clap and anyhow. This library uses none of them.
The options --tab-width, --space-after-colon, --escape-slash, --eol, --no-final-newline, --jsonl and -o of the prototype were dropped.
Line endings are LF only, and exactly one newline is always added at the end.
The --max-width of the prototype did not count the length of keys, so lines ran past the width.
Here keys are counted.
The prototype kept only the later value when a key appeared twice. This library keeps both.
cargo test
tests/format.rs checks the formatted output, and tests/parse.rs checks the position and wording of errors on broken JSON.
compare/ holds Python scripts that check the command from the outside, and a program that compares speed. It is not part of the package. The scripts need Python 3.12 or later. Build the release binary first, and fetch JSONTestSuite and the speed data with fetch.py before running them.
cargo build --release
python3 compare/fetch.py fetch JSONTestSuite and the speed data
python3 compare/suite.py run all 318 files of JSONTestSuite
python3 compare/random_test.py compare random JSON with json.dumps of Python, character by character
python3 compare/sanity.py check that the comparison catches deliberate differences
python3 compare/fuzz.py compare acceptance with Python on 5000 pieces of broken JSON
python3 compare/long_string.py compare long strings placed at the --max-width boundary
python3 compare/cli_test.py options, exit codes, -w and --check of the command
python3 compare/attack.py hostile input, and how large memory grows against the input
python3 compare/leak.py memory leaks (macOS only)
python3 compare/gen_big.py make large JSON in compare/big/ to check speed
leak.py needs cargo build --release --manifest-path compare/bench/Cargo.toml --bin repeat first.
Dual licensed under MIT and Apache-2.0. Take whichever you prefer.