Releases: nao1215/jsonize
Release list
v0.9.0
This release adds sixteen definitions (the registry holds 280 commands through 758 definitions, from 270 and 742), makes an option either change the answer or be refused before anything is read, keeps the headings of a csv read as data as written, and fixes readers that returned JSON different from their input: YAML, JSON Lines, LTSV, local timestamps, folded lines and floats too small to hold.
Added
rsync --stats(rsync/stats): the transfer counts rsync prints at the end of a run, with the summary lines that follow them, as one object of typed numbers. The counts by kind that rsync prints in parentheses are read as an object of their own, and a kind it leaves out is null rather than zero.-v,-i,--progressand two or more-hoptions print something else and are refused.7z l -slt -ba(7z/list-technical-bare): the entry blocks 7-Zip prints with-ba, which leaves out the banner, the scan line, the archive name and the archive's own properties. An entry is the same object7z/list-technicalgives it; what differs is the whole, which is the array of them rather than an object holding the archive beside them.openssl x509 -noout -text(openssl/x509-text): the version, the serial, the signature algorithm, the issuer, the subject, the two dates, the kind of key and its size, and one record per X509v3 extension with its critical mark and its value as printed. The key material and the signature are hexadecimal folded over a dozen lines or more and are not read;input.ignorenames those lines, so--explainreports how many were left out.tesseract IMAGE stdout tsv(tesseract/tsv): one row per page, block, paragraph, line and word, with the box each occupies and, for a word, its confidence. A row that is not a word prints -1 for the confidence, which is tesseract's own value and stays the number it printed.hocr,alto,pdfand the plain text tesseract writes when no configuration is named are other formats and are refused.- Seven definitions for time zones, the SSH client and the desktop:
zdump(zdump/current) andzdump -v/-V(zdump/verbose, which keeps the lines where gmtime or localtime failed, with the seconds they failed on),ssh -G HOST(ssh/config-dump, a list of name and value, so a keyword given more than once such asidentityfileorlocalforwardkeeps every value),xwininfo(xwininfo/stats),usb-devices(usb-devices/linux, one object per device with its interfaces and their endpoints),glxinfo -B(glxinfo/brief) andeglinfo -B(eglinfo/brief, one record per platform and per device, holding the errors a platform printed). - Five definitions:
lscpu -p(lscpu/parsable, the columns named by the header comment, so any-p=LISTis read, and a row whose cache IDs lscpu left out is refused rather than shifted),gcc -print-search-dirs(gcc/print-search-dirs, also underg++, with the program and library paths as arrays),apt-cache rdepends(apt-cache/rdepends, marking an alternative and the other packages that satisfy the same dependency),pw-cli ls(pw-cli/list-objects, one record per PipeWire object with its properties), andxprop -root/xprop -id(xprop/default, one record per property, with the lines printed under it kept). The registry holds 280 commands through 758 definitions. - A server-side group in the cookbook: inspecting an uploaded PDF with
pdfinfo, an archive with7z l -slt, a certificate withopenssl x509, an upload's type withfile -i, and recording what a backup run moved withrsync --stats, closing with thejz runexit-status contract and what converting the output does not do. The five commands run for real in the end-to-end suite on Linux, over paths holding a space and shell metacharacters, with their failure cases. group_separatoron an int or a float field: the one character a format writes between groups of three digits. It is removed only where the digits are grouped by it, so a value of another shape is an error rather than a number made by dropping characters. It is for a format that decides the character itself; a count printed with the separator of the user's locale still stays the string it was printed as.
Changed
- An option either changes the answer or is refused with exit 2 before anything is read or run.
--raw,--assume-yearand--assume-zoneare now refused for a data format (--formator a data file's extension), which is read without a definition's field rules and so could change nothing, and--assume-yearand--assume-zoneare refused when no variant of the named parser, or the--define, prints a timestamp they could complete (jz run --assume-year now df), the way a key to--extractnone of them has already was.--assume-yearand--assume-zonebeside--raware refused as--typebeside it is, since--rawleaves a timestamp as the text it was cut as. When the definition is found from the text, an option it has no use for still passes, since which definition reads the input is not known until it has been read. - The header line of a csv or tsv read as data (
--format csv, a.csvfile,jz new k:=@x.csv,--stream) is now the keys as written:名前,In Stockand%CPUstay as they are, where they becamecolumn,in_stockandcpu_percent. An empty heading is stillcolumnand a repeated onea_2.--parser csv, which reads with a definition, normalises the headings as before. jz list df/gnuandjz list --schema df/gnutake the name--explainandjz listprint for a definition. A path such as/usr/bin/dfis still read as the program it names.- An ini section heading with no name (
[]) is an error. It merged into the keys written before the first heading, which go under the empty name.
Fixed
apt-cache/dependsrefused the line of a relationship ORed with the next one (|PreDepends: coreutils-from-uutils), sojz run apt-cache depends coreutilswas exit 3. The mark is now read intoalternative, which is true for such a relationship and false for the others.apt-cache/dependsclaimed the output ofapt-cache rdepends -o APT::Cache::ShowDependencyType=true, then failed on itsReverse Depends:line with exit 3. It now leaves that output toapt-cache/rdepends.gpg/list-keysrefused a user ID whose validity gpg pads on both sides ([ full ]), which is how a key signed into full validity is listed, with exit 3.- A
timefield withlocation: localmoved a wall clock the zone skips to another hour. WithTZ=America/New_York,2025-03-09 02:30came out as2025-03-09T01:30:00-05:00, the same answer as for01:30, becausetime.ParseInLocationnormalises the hour a change to summer time skips. The value is now refused with exit 3. input.foldjoined a continuation onto a blank line above it, soa, an empty line and an indentedbgave a record" b"with a space the text does not hold. A blank line is now nothing to join onto, in the whole reading and in--streamalike, and the continuation is an error naming its line.- A
floatfield, and--type COLUMN=float, wrote0for a value too small for a 64-bit float (1e-400), while one too large was refused. Both are now refused with exit 3. - The error for a
treeline that skips a level gave the wrong levels:afollowed by a line indented twice said "indented 1 levels below a line at level 1". It now says the line is at level 2 under a line at level 0, and a first line that is indented says there is no line above it. jz list dffdid not suggestdf, althoughjz run dffand--parser dffdo.jz listnow gives the same suggestion.- YAML: the value of an explicit key written on the line of its ":" was read as one string when it opened a collection there.
? afollowed by: - xand an indented- ygave{"a":"- x - y"}with exit 0; it is now the list["x","y"], and: b: 1is a mapping. - YAML: a sequence item written on the line of its key,
a: - b, was read as the string"- b". The same happened with- 1inside a flow sequence and with? bused where a value belongs. YAML does not allow a plain scalar to start with an indicator and a space, and other readers refuse the line; jz now refuses it with exit 3 and says to start the items on the next line. - YAML: a comment did not end a plain scalar written over several lines.
a: x # notefollowed by an indentedygave"x y", keeping the text after the comment; the indented line is now refused as unexpected indentation. - YAML: a literal or folded scalar at the end of a text without a final line break gained one.
a: |overxwith no newline after it gave"x\n"; the break a scalar keeps is the one its last line has, so it is now"x". - JSON Lines, LTSV and
jz new --eachskipped any line of Unicode white space. A line holding only a no-break space, an ideographic space, a form feed or a vertical tab disappeared with exit 0, although none of these is white space in JSON, and--format jsonrefuses the same text. Only a line of JSON's white space, spaces, tabs and carriage returns, is blank in JSON Lines now; LTSV skips only an empty line and, as its reference says, refuses a line of spaces as a field without a label. jz run go test -bench=Parsewas exit 4.go/benchrequired-benchor the literal-bench=., and an argument written with=is matched by its name and the=, so any other value left it without a candidate.go/benchnow takes-bench=and the two-dash spellings, andgo/testrefuses them.FuzzReadJSONreported an escape naming half of a surrogate pair as valid JSON the reader refused, somake fuzzfailed on every run that reached that input.json.Validaccepts such a document andjson.Unmarshalthen hands back U+FFFD, which is a character the input may hold for itself, so refusing it is what keeps a conversion from changing what it was given. The fuzz target now names it among the refu...
v0.8.0
This release adds five Git inspection reports, holds every document built by jz new to the same nesting limit as documents jz reads, and replaces the in-process Go benchmarks with whole-command himorime suites that cover latency, CPU time, peak memory and throughput.
Added
- Five Git reports:
git clean -n(git/clean-dry-run),git diff --summary(git/diff-summary),git ls-files --eol(git/ls-files-eol),git notes list(git/notes-list) andgit submodule status(git/submodule-status). The registry holds 270 commands through 742 definitions.
Changed
- Performance is measured only with himorime.
bench/himorime.yamlruns jz as a pipeline does, one process per call: start-up, detection with and without a named parser, text no definition reads, registries of 26 to 1000 extra definitions and--define, 100 000-rowps,df,mountandenvoutput, every data format, csv key selection,--stream,-pandjz new, for latency, CPU time, peak RSS and throughput. A pull request is compared with its base on the same runner and fails on a confident regression.bench/compare/himorime.yamlmeasures jz against jc and jo, andmake bench-docsrewrites the tables of the comparison page from it. The Go benchmark functions,bench/baseline.txt, the benchstat comparison andscripts/compare_bench.share gone.
Fixed
jz new --pathcould build a document nested past the 1000-level limit that the same document read as JSON or YAML refused. The completed document, including containers made by a location and values read from files or--each, is now held to that limit with exit 3 and no output.
v0.7.1
A performance release with no change to what jz reads or writes. jz run COMMAND and --parser NAME read only the definitions that answer to the name, so a short conversion with a named parser takes about a tenth of the time it did, and csv files and tables with many columns are read in about half the time with less memory. The documentation site has a page comparing jz with jc and jo, with the benchmark script that produced its figures.
Added
- A comparison with jc 1.25.7 and jo 1.9 on the documentation site and in the README: what each reads and builds, where each is the better choice, what jsonize does not do, and timings and peak memory measured on all cores and on one core with
scripts/compare_bench.sh, including the cases where jc or jo is faster.
Changed
jz run COMMANDand--parser NAMEread only the built-in definitions that answer to the name, not all 737. On a shortdf -hreport pinned to one core,jz --parser dftakes about 5 ms instead of 46 ms andjz run df -habout 6 ms instead of 47 ms. Detecting the format from the text still reads every definition. With a registry of your own, every definition is read as before.- Reading a csv allocates a quarter of what it did and takes about half the time: every record was read through a new 4 KB buffer, and one buffer now serves the whole input.
- A table whose rows hold more than eight columns, such as
ps auxortop, no longer builds a map for each row, and a process time such as4:50is added up in whole seconds unless it has a fraction. On 100,000 rows ofps auxjz takes about half the time and a third less memory.
v0.7.0
The registry reads 270 commands through 737 definitions, up from 227
through 600: sar's reports, gpg keys, git's branches and config, file
system and partition tools (dumpe2fs, wipefs, gdisk, partx), CUPS's
lpstat and more. What was read wrong with exit 0 is refused instead: a
csv whose lines end with a carriage return alone, and YAML text after a
flow collection. A csv record read as data is bounded by the input limit
like every other format, an option error names the option the way the
help writes it, and top -b -n N is a list with a record per iteration.
Added
- Twenty-three commands:
dumpe2fs -handtune2fs -l(the superblock
of an ext2/3/4 file system),e2freefrag(free space summary and
histogram),wipefs(the signature table, and the report--all
prints),partx --show(with rounded sizes and with-b),gdisk -l
andsgdisk -p(a GPT listing),filefrag -v(the extents of each
file),fincore(with rounded sizes and with-b),diffstat,
gocyclo(with-avgand-avg-short),exif(libexif's table),
isoinfo -d,cmpandcmp -b,cloud-init status --long,
pro statuson a machine with no subscription (also asua),
oomctl,pinkyandpinky -l,sttyandstty -a,
setxkbmap -query,udisksctl status,lpstat -p,-vand-t,
andcpupower frequency-info.git diff --statis no longer read
fromdiffstatoutput, whose count column git never prints at that
width. The registry holds 270 commands through 737 definitions. - net-tools
arp,arp -nandarp -i IFACE(arp/net-tools), the
cache as a table, andifconfig -s(ifconfig/net-tools-short).
arp -areads an entry published for a proxy, which net-tools prints
with<from_interface>for its hardware address and was refused.
net-toolsroute -ee(route/linux-extended) androute -A inet6
(route/linux-inet6), androute -ethroughnetstat/routing. A
rejecting route, whose counts net-tools prints as-, was refused by
route/linuxandnetstat/routing; the counts are null now, and the
published schemas are unchanged, since they already allowed it. The
registry holds 247 commands through 704 definitions. numactl --show(the NUMA policy and the processors and nodes a
process is bound to) andtaskset -p(the affinity mask of a process,
beside the list taskset -pc already gave), andresolvectl query
(the answers for each name, with the link, the protocol, how long it
took and where the data came from). The registry holds 247 commands
through 707 definitions.gpg -kandgpg -K, each key with its algorithm, dates,
capabilities and fingerprint, its user IDs with their validity, and its
subkeys. The registry holds 247 commands through 700 definitions.dpkg-deb -I(a package's size, the members of its control archive
and its control fields) anddpkg-deb -c, which prints the listing
tar/gnu reads, anddpkg-divert --list. The registry holds 247
commands through 699 definitions.readelf -s -W, each symbol table with the value, size, type,
binding, visibility, section and name of every symbol, and the version
a symbol is bound to. The registry holds 245 commands through 697
definitions.- Three commands of poppler-utils:
pdfinfo(the document information,
page count, page size and version),pdffonts(each font with its
type, encoding and whether it is embedded) andpdfimages -list
(each image with its size, color space and encoding), andtrust listfrom p11-kit (each certificate of the trust store). The registry
holds 245 commands through 696 definitions. - The checksum lines of more tools:
cksum -a ALGORITHM(tagged, and
with--untagged),cksum -a bsd,shasumwith no algorithm,-a 1,
224,384and512and with--tag, andb2sum -l BITSfor 128,
256 and 384 bits. Each goes to the definition whose line it prints. git config --list --show-originand--show-scope(the scope, the
kind of origin and the file of each setting),git diff --shortstat
andgit ls-tree -l(the size of each blob). The registry holds 241
commands through 692 definitions.go tool cover -func(the coverage of each function and the total),
git status --porcelain=v2(the branch headers, and each entry by
kind with its modes and object names), andgit log --numstat,
--name-statusand--name-only(andgit showwith them), which
before failed with exit 3. git/log now refuses those forms and-p
with exit 4. The registry holds 241 commands through 689 definitions.- Seventeen definitions. Eight commands jz did not read:
apt-config dump,avahi-browse -p(and with-rthe resolved host, address,
port and TXT record; a browse left running is read as a stream),
fc-match,gio mime TYPE,gpgconf --list-dirsand
--list-components,gsettings list-recursively(a value kept as the
GVariant text printed),pwdx(read only when named) and
systemd-delta --diff=false. More of three it did:git diff --name-status(a quoted path decoded),git reflog,git ls-remote,
git cherry,systemd-analyze timespan,timestampandcalendar,
andresolvectl dnsanddomain. - Three more:
sum(the BSD checksum, told from cksum by its padding),
ipcs -l(a record per resource) anddf -Pwith-B,-mor
--block-size(the unit the heading names in bytes). The registry
holds 236 commands through 620 definitions. sar -d, the activity of each block device, and for ELF files
readelf -S -W(the section headers),readelf -d(the dynamic
section) andsize -A(the size and address of each section), and
rustc -vVandcargo -vV, and BusyBoxtop -b(a virtual size
that runs into the percentage beside it is told apart). The registry
holds 237 commands through 627 definitions.- Twenty-five
sarreports:-W,-H,-v,-I,-u ALL,-r ALL,
-m CPU, and with-nthe reportsEDEV,NFS,NFSD,SOCK,
IP,EIP,ICMP,EICMP,TCP,ETCP,UDP,SOCK6,IP6,
EIP6,ICMP6,EICMP6,UDP6andSOFT,pidstat -U, the
per-task report with user names, andiostat -x -d, the extended
device counters without the CPU table, andvmstat -p PARTITION
(655 definitions). - Network settings:
ethtool -g(ring sizes) andethtool -a(pause
frames),ip route get,nmcli -t device,iw reg get(the
regulatory rules per frequency range) andhostname -I. pmap -x(a record per process with its mappings and totals),
pgrep -landgetent ahosts.rpm -qiandrpm -qia(rpm/info), one object per package with
the description kept whole, read from rpm 4.14, 4.16 and 6.0 output;
git log --statand--shortstat(git/log-stat), each commit with
its diffstat asfilesandsummary;ifconfigfrom net-tools
(ifconfig/net-tools) and from macOS and FreeBSD (ifconfig/bsd);
and macOSarp -a(arp/darwin)./proc/pressure/*(pressure stall averages),/proc/self/io,
/proc/cgroups,/proc/net/sockstat,/proc/PID/mapsand
/proc/net/if_inet6.apt-cache show(a record per package version, sizes typed), and
snap refresh --list,snap aliasesandsnap version.- For git,
branch(which branch is checked out here or in another
worktree),status -sb(the branch, its upstream and how far ahead or
behind, then the changed paths),worktree list --porcelainand
count-objects -vH; andgo version. The registry holds 241 commands
through 684 definitions. jz run --format NAMEreads the command's output as a data format,
the wayCOMMAND | jz --format NAMEreads it, with--stream,
--columnsand--typeas there.jz run --typealready told the
caller to give--format csv, whichjz rundid not take.
Changed
jz run --helplists--format,--columnsand--typeunder
Input and the parser options under the same heading asjz --help,
where--columnsand--typewere among the parser options and
--formatamong the options for the command.npm ls -gandnpm ls -g --allare read. The first line is the
global directory with no package name, so the project'snamein
npm/ls and the root'snamein npm/ls-all may be null, and both
schemas are version 2. Before, the output was refused with exit 4.go list -m -u allis read: go/list-modules addsupdate, the newer
version shown in brackets,retractedanddeprecated, and the same
three for the replacement. Before, the first line with any of them
failed with exit 3. Every object now has these keys, so the schema is
version 2.git branch -ris read, andgit branch -awhen the current branch
is past the twentieth line. Both were refused with exit 4, since the
signature asked for a line marked current.- An
argsentry that ends in=also matches the one-dash spelling
of Go's flag package:any: ["-func="]takes-func=c.out, which
before was only split into letters. git log --statoutput named as--parser git --variant logis
refused by the signature (exit 4) instead of failing inside the message
(exit 3), and detection hands it togit/log-stat. The published
schema of git/log is unchanged.- A file path names more definitions: a dot in the file name is looked
up as a dash (jz --file /etc/resolv.confreads with etc/resolv-conf)
and a file one directory down has that directory joined to its name
(/proc/net/devis proc/net-dev,/proc/self/iois proc/self-io).
Before, both went to automatic detection, which refuses formats that
are read only when named. A capture saved asfstab.txtstill names
nothing. iostat -sandiostat -xsprint short forms whose columns
(kB_w+d/s,kB/s,rqm/s,await,areq-sz,aqu-sz,%util)
iostat/linux read as strings; they are numbers now, and the published
schema of iostat/linux is version 3.top/linuxis a l...
v0.6.0
A stream ends at the first record it cannot read, so the next program in
a pipeline never sees a partial result that looks complete, and
--stop-on-error goes away with the choice it offered. jz new KEY:=JSON
returns the same status as the same text read from a file, and the
registry reads what exiftool, identify and pkg-config print: 227 commands
through 600 definitions.
Added
- Four definitions for what three tools print:
exiftool FILE(the tags
one per line) andexiftool -G FILE(the group in front of each of
them),identify FILE...(one record per image, its width and height
typed), andpkg-config --list-all(the modules it knows, read only
when named since a name and a phrase is also what a glossary looks
like). The registry holds 227 commands through 600 definitions.
Fixed
jz new KEY:=JSONreturns 3 rather than 2 when the argument is JSON
and jz refuses what it holds: a key given twice in one object, or
nesting past the depth limit. The same text in a file (KEY:=@f.json)
and the same text read as data (jz --format json) already returned 3,
so a script that built the argument with a command substitution got a
different status from one that passed the file. Text that is not JSON
at all is still 2, since that is the command line being wrong.
Changed
- A stream ends at the first record it cannot read, rather than leaving
the record out and going on.--stream,jz run --streamandjz new --eachall do this, for a command's output, a data file andlinesor
nulalike: the records written before the failure stand, it is
reported once on standard error, nothing after it is read, and the
status is 3. A stream that goes on past a record it dropped reaches the
next program in the pipeline looking complete, since the diagnostic
went to standard error and the status comes only after the records have
been read and acted on.
Removed
--stop-on-errorasked for what every stream now does. A command line
that still names it is refused with exit status 2 and a message saying
so; drop the option and the behaviour is unchanged.engine.Streamno longer takes anonError func(*ParseError) error
besideemit: with one answer to a record that cannot be read there is
nothing to choose. A caller passingniltoday drops the argument and
gets the same reading.
v0.5.0
jz holds every reading of an input to the same rules. A stream is
identified under the record limit it is read under and drops a file path
that does not fit, the way a whole document does; a JSON escape that
names no character and a control character standing in YAML text are
refused rather than replaced; YAML nests as deeply as JSON. A table can
ask for its rows to be counted instead of letting the last column take
the rest of the line, jz test --json writes what failed as one object,
and loading the registry costs a tenth less time and a fifth fewer
allocations. The registry is unchanged at 224 commands and 596
definitions.
Added
jz test --jsonwrites the result to standard output as one object:
the counts, and a failure per entry with the definition, the case, the
fixture path, the registry it came from, what it failed at and the
message. The kinds are load, select, parse, refuse, stream, unread,
schema and golden, so a job can tell one failure from another without
matching the message. The lines on standard error and the exit status
are the same with the option as without.
Changed
max_fieldsof atablemay be one more than the number of columns,
which is how a definition asks for a row to be counted rather than for
its last column to take the rest of the line.ps auxkeeps the
default;proc/diskstatsnow counts every row, where a twenty-first
field used to be caught only by failing to convert to an integer in
the last column.- Loading the registry costs about a tenth less time, a tenth fewer
bytes and a fifth fewer allocations. The walk that looks for
definitions no longer descends into the fixtures beside them (5,614
files were read through to find 596), a definition file is read into
one buffer of its own length, and the YAML reader takes its nodes from
blocks, leaves text with no CRLF in it where it lies and does not
build a scalar written on one line. Nothing about what is chosen, what
is reported or which registry wins changes.
Fixed
- The examples that append to an array are quoted, in the README, the
usage guide, the cookbook,jz new --helpand the demo recording.
jz new tags[]=webcopied into zsh, which is what macOS starts with,
was refused by the shell withno matches foundbefore jz saw it. --streamholds the lines it identifies the format by to the 1 MiB
record limit that the reading of a record already applied, so a
producer that never ends a record is refused at the byte past the
limit with exit 3 instead of waited for. Two megabytes with no line
break were refused at once with--defineand waited for with
--parser df --variant gnu.--stream --filedrops a file path that named a definition the text
does not fit, the way the whole-document reading does, and reads the
text on its own terms. A df report saved as/etc/fstabwas exit 4
with--streamand exit 0 without it.- Input that cannot be read at all, such as a gzip file whose checksum
does not match, ends--streamwith the status the whole-document
reading ends with: exit 3 rather than exit 1. - A
\uescape naming half of a surrogate pair in JSON or JSON Lines is
refused with the line it is on, where it was read as U+FFFD and
written out with exit 0:{"name":"\ud800"}became{"name":"\ufffd"}.
A pair written in full and a replacement character the text holds for
itself are unchanged. - A control character standing in YAML text is refused, as YAML 1.2.2
does not allow it there.key: ab<NUL>cdwas read as a value holding
it. One written as an escape ("a\0b") is still a character of the
value. - YAML nests as deeply as JSON: both are held to 1000 levels of arrays
and objects, where YAML stopped at 100 and counted a scalar as a
level. - JSON, JSON Lines, YAML, LTSV,
linesandnulinput, read with
--format, a file's extension orjz new key:=@file, is held to the
4,194,304 values a csv and a definition's reading were already held to:
a JSON array of 4,194,305 numbers was read with exit 0. Past the limit
a whole reading is exit 3 with nothing written. A stream counts each
record, and a record past the limit is a record that cannot be read,
with the records before it kept. jz newcounts the values of the whole document it makes, so files each
under the limit cannot make one past it.--eachcounts each document
and treats one past the limit as a line that cannot be read.jz newreports a file or standard input past the 64 MiB limit with
exit 3, as--filedoes, where it was exit 1.- Without
--extractor--exclude, output is no longer copied object by
object, and no option remembers every key it has seen: a stream of JSON
Lines whose records each have keys of their own grew from 23 MiB to
162 MiB of memory between 100,000 and 1,000,000 records, and now stays
at 12 MiB with or without the options. A refusal of an unknown key
names at most 64 of the keys the input had.
v0.4.0
jz is the step that makes JSON for the next command. jz new places
strings, file text and JSON values at keys or JSON Pointers, and makes one
document per line of standard input with --each; plain text reads as
strings with --format text, lines and nul; a csv or tsv column
takes a type with --type. --yaml output is removed. The registry is
unchanged at 224 commands and 596 definitions.
Added
jz new --eachmakes one document per line of standard input and
writes each as a line of JSON when its line has come, holding nothing
else of the input:vmstat 1 | jz --stream | jz new --each host=a sample:=@-.KEY:=@-is the line read as JSON,KEY=@-the line as a
string, and the files the other arguments name are read once. A line
that cannot be read is reported and left out, with the status 3.--stop-on-errorends a stream (--stream,jz run --stream,jz new --each) at the first record it cannot read, keeping the records written
before it, with the status 3; a commandjz runstarted is stopped.--format text,--format linesand--format nulread input that
has no format of its own as strings: the whole input as one string, a
list with one string per line (LF or CRLF), or one per NUL-terminated
record asfind -print0writes them. Nothing is trimmed, empty records
stay"", and a separator at the end does not add an empty record.
--streamwriteslinesandnulrecords as they arrive.--type COLUMN=TYPEconverts a csv or tsv column toint,floator
boolwith the rules a definition's field of that type follows; an
empty value is null and the other columns stay text. A value that is
not the type is exit 3 with the line and the column, and a column the
input does not have is exit 2.jz new --string KEY=TEXTwrites TEXT as a string exactly as it is, so
a value from a variable is never read as a file name (@...) or JSON,
whatever it holds:jz new --string "message=$MESSAGE".jz new --text-file KEY=PATHputs a file's text, or standard input's
with-, into the JSON with every line ending kept.KEY=@PATHstill
drops the last one. Text that is not UTF-8 is exit 3.jz new --path POINTER=VALUEplaces a value at a JSON Pointer with
the operators a plain argument has (=,:=,=@,:=@), making the
objects and arrays on the way;-appends and an index names an
element already given.--stringand--text-filetake a pointer in
place of a key. A location given twice, a pointer into a value given
whole, a key in an array and an index past the end are exit 2 before
anything is read.spec.replicas:=3is still the keyspec.replicas.
Changed
jz --helpgroups the options under Input, Output and the parser of
command output, says what jz is for in one line, and shows examples of
every source of JSON;jz run --helpandjz new --helpgroup theirs
the same way.--rawis described as what it is, the text a definition
cut without its field rules, and--extractand--excludeas keeping
and dropping keys of each object.- The README, the landing page and the usage guide describe jz as the
step that makes JSON for the next command, and every jz command they
show with its output is run by the end-to-end suite
(e2e/atago/docs.atago.yaml); a test fails when a page shows one that
no scenario runs. The--explain=jsonexample said the df capture had
8 lines read, where 7 are read and one is left out. - A data file read with
--stream(jsonl,ltsv, and the newlines
andnul) leaves out a record it cannot read and goes on, as a stream
of a command's output does, where it ended at the first. The status is
still 3. Pass--stop-on-errorto end at the first as before.
Removed
- Breaking:
--yaml, which wrote the output as YAML, is gone from the
default mode,jz runandjz new. jz writes JSON only, and a command
line that still passes--yamlis refused with exit status 2 and a
message saying so, before anything is read or run. To keep getting
YAML, pipe the JSON to a YAML tool:df -h | jz | yq -P. With
--stream, read the JSON Lines one document at a time. Reading YAML is
unchanged: a.yamlor.ymlfile,--format yaml,jz new key:=@file.yamland definitions written in YAML all work as before.
Fixed
- A csv or tsv read as data (
--format csv, a.csvor.tsvfile,jz new key:=@file.csv) keeps what it holds: an escape sequence stays in
its value, where it was taken off as colour, and a line of spaces is a
row, where it was dropped (printf 'key\n \nx\n' | jz --format csv
gave one row). A csv definition in the user registry no longer changes
such a reading, which made007a number with--format csvand left
it"007"withjz new; only--parser csvreads with it.--explain
now says the file was read as csv rather than namingcsv/comma.
v0.3.0
jz makes JSON from more than command output: from data files (CSV, TSV,
LTSV, JSON Lines, JSON and YAML, also gzip or bzip2 compressed) and from
arguments given to jz new. The registry is unchanged at 224 commands
and 596 definitions.
Added
jz newmakes a JSON object from its arguments, or an array with
--array:key=textis a string,key:=jsona JSON value,
key=@patha file's text,key:=@paththe value a data file holds and
key[]=one more element of an array. Nothing is guessed from a value,
and a key given twice without[]is exit 2.- Data files:
jz --filereads a file as the format its extension
names,.csv,.tsv,.ltsv,.jsonl(or.ndjson),.jsonand
.yaml(or.yml), each also compressed as.gzor.bz2, and
--format NAMEreads piped data as that format. JSON keeps its key
order and number literals, YAML is typed by the 1.2 core schema, and a
key given twice, text that is not UTF-8 or a value JSON cannot hold is
exit 3 with the line.--streamwrites JSON Lines and LTSV records as
they are read.--explainsays which format was read and what named
it, and--explain=jsoncounts the values inread.values. - The site has a Cookbook
of 20 tasks, each run by the end-to-end suite, and a test fails when a
documented command, the options block, the exit-code table or a demo
tape no longer matches the command line.
Changed
- A file given with
--filewhose extension names a data format is read
as that format, where its text was detected as command output. Name
--parseror--defineto read such a file as a command's output;
a file with any other extension (captured.txt) is detected as before.
v0.2.0
145 definitions for 55 more commands, 224 commands and 596 definitions
in all, the system commands of Windows and macOS among them. Where one
command's output is followed by another's, more definitions now refuse
the text instead of reading the other command's lines as their own.
Added
- A tree definition can state what its top-level line looks like with
parse.root. The node alternatives read every depth, and the one
that reads the continuation of a wrapped list reads any text, so a
word printed afterlspci -v, or another command's output piped in
behind it, was read as one more device with no children; withroot
it is refused, naming the line.lspci -v,lsusb -v,lsusb -t,
iw dev,apt-cache depends,systemctl list-dependenciesand
systemd-analyze critical-chainstate theirs. - Definitions for 25 more traditional and modern commands. Archive
listings:gzip -land-lv,xz -land--robot -l,zstd -land-lv,lz4 --list,7z land7z l -slt.
Checks and keys:md5sum -cand the other digest commands'-c,
ssh-keyscan,ssh-add -L(andssh-add -lasssh-keygen -l),
openssl x509 -nooutwith-subject,-issuer,-dates,-serial
and-fingerprint,passwd -S,screen -ls. System:snap services,
snap connectionsandsnap changes,lsipc -q,-mand-s,
nmcli radio,timedatectl show,loginctl show-user,show-session
andshow-seat,apt-cache search,dpkg-query -W,pip freezeand
pip list --outdated. Developer tooling:go testresult lines and
go test -benchreports,gh pr list,issue list,run list,
release list,workflow listandrepo list,rustup check,
cargo search,just --list,uv python list,mise outdated.
Containers and clusters:docker system dfandsystem df -v,
docker context ls,docker compose lsandcompose ps,
docker ps -s,docker version,kubectl get pods,nodes,
services,deploymentsandnamespacesin their default, wide and
all-namespaces forms,kubectl config get-contexts,
kubectl api-resources,kubectl version,rclone lsl,lsdand
version,redis-cli infoandclient list. - A top-level
input.select.untilthat states its whole line reads that
line as the end of the output, the wayafterreads a heading, and a
line after it is unread (exit 3).net sharecloses on its completion
line with it, so another command's output behind the listing is
refused rather than read as shares. unescapetakesoctal: true, which reads a backslash and three
octal digits as the byte they name, andquote, which decodes only a
value put between that character. git and getfacl write names that
way.- A definition for
systemd-cgls: the control groups under the group,
unit or directory it starts from, the processes in each with their
ids, whether a group is delegated, and the group ids and extended
attributes systemd 252 prints to root. In atree, a form of
indentation that ends in a branch character (|-,└─) ends the
indentation, so the blanks after it that pad a right-aligned number
belong to the node. - Definitions for
eza -l(with--header,-a,-B, and in its
long-iso, full-iso,-gand-Hcolumn sets),procs,tokei,
bat --list-languagesandhyperfine --style basic. - Definitions for macOS:
vm_stat,sw_vers,diskutil list,
launchctl list,pmset -g,netstat -iandnetstat -rn,
system_profiler SPSoftwareDataTypeandSPHardwareDataType,
scutil --dns,networksetup -listallhardwareports,
brew outdated --verbose,memory_pressure,top -l,otool -L,
lipo -info,csrutil status,spctl --status,fdesetup status
andshasum -a 256, from output captured on a macOS 26 runner. - Definitions for Windows:
tasklistin its table,/v,/svc,/m,
/fo listand/fo csvforms,netstat -an,-anoand-e,
route print,arp -a,getmac /v,driverqueryanddriverquery /v,sc queryandsc queryex,schtasks /queryas a table and as
a list,net user,net localgroup,net shareandnet start,
whoami /groupsand/priv,chcpandver, from output captured on
Windows Server 2022 and 2025 runners. - Definitions for compiler-style diagnostics:
go vet(andgo build),
staticcheck,golangci-lint,actionlintandshellcheck -f gcc;
and formc ls(the MinIO client) andtask --list. - Definitions for
cargo tree,uv tree,npm ls --all,busctl tree
(one service or several),docker buildx lsandxinput list. md5sum --tag,sha1sum --tag,sha256sum --tag,b2sum --tagand
the rest of the GNU and uutils family are read by the definition of
the BSD digest line, which they print, together with FreeBSD and macOS
md5. A name the tools escape is decoded and markedescaped.- When
jz runhas no parser for a command and nothing in its arguments
names one, the refusal shows how to name one:
jz run --parser PARSER -- COMMAND .... split: alignedreads a heading of several words, such as
CONTAINER IDorSoft Limit, as one column whenheader.columns
gives it the name those words derive together.- Definitions for
lshw -shortandlshw -businfo. - Definitions for FreeBSD
md5and the other BSD digest commands,
sysctl,vmstat -i,df -hTandarp -a, and for OpenZFS
zfs listandzpool list. - Definitions for FreeBSD
kldstat -h,netstat -ib,netstat -r,
sockstat -s,last -yandiostat -x, and forlddoutput that
names each program above its libraries (glibc given several programs,
FreeBSD always). - With
--streamand--explain=json, a record the stream leaves out is
reported when it is left out, on its ownjz: explain:line of
standard error:{"event":"skipped","definition":...,"line":..., "reason":...,"skipped":N}, whereskippedcounts the records left out
so far.--explainwrites the same fact as a line of text. - Releases sign
checksums.txtwith cosign, include an SPDX SBOM for
each archive, and publish a Homebrew cask tonao1215/homebrew-tap.
A tag is published only after the release smoke checks pass on it.
Fixed
chage -l,curl --version,git count-objects -vandethtool -i
are read only as the lines they print,readelf -honly as the fields
of an ELF header, andlscpu --cachesholds each row to a cache name
and type. Lines another command printed after them were read as more
fields and as more caches. Thereadelf -hschema names the fields it
can hold, and is version 2.systemctl list-sockets,list-paths,list-timers,
list-automounts,list-machines,list-unit-files,list-jobsand
list-units,networkctl list,loginctl list-sessions,
list-seatsandlist-users,systemd-inhibit --listandtree
read their count line ("29 sockets listed.", "3 directories, 5
files") as the end of the listing. Another
command's output after it is refused as unread (exit 3), where rows of
it that fit the columns were read as more entries.jz run sysctlandjz run lsofon macOS found no definition: both are
the programs FreeBSD and Linux have, printing the same lines, and their
definitions now apply there.ls -leandls -lOon macOS, which add
access control lists and file flags to a long listing, are refused by
the argument rather than failing on the first list.- Ctrl-C typed at a terminal reached the command
jz runstarted twice:
once from the terminal, which sends it to the whole foreground group,
and once passed on by jz. A command that stops at once on a second
interrupt was stopped by one keypress. jz no longer passes on an
interrupt when the terminal's foreground group is its own and the
command is in it. jz run ls -lfor a user whose environment setsQUOTING_STYLE
returned names with the quotes in them. jz runs ls with the literal
style, and the long listings refuse-Q,--quoting-style,-band
-q, which change the names they print.jz run git log --onelinein a repository or for a user whose
configuration setslog.decoratereturned the branch names as part of
the first subject. jz runs git with decorations and signatures off for
this format, refuses-cand--config-envamong its arguments, and
on a pipe refuses a line whose parenthesis opens withHEAD, a tag or
a remote branch. Anargsentry that ends in=matches the long
option with any value.
exec.envnow comes from the variants the arguments leave, so the
setting does not reachgit config --list.- A duration with a fraction of a unit under a second is the number the
text names:1.3 msis0.0013where it was0.0013000000000000002,
andsystemd-analyze critical-chainreports+87msas0.087. The
parts of a duration are added up exactly and turned into a number
once. jz run systeminfo /fo csvno longer suggests that systeminfo runs
csv. A name whose definitions only describe a shape (csv,table)
is an option's value when it appears among a command's arguments.jz run systemctl list-units --plainfailed with exit 3 while a unit
had a job pending.--plaindrops the marker column, and the
definition for the table with a JOB column expected the header to be
indented for it. That table now has its own definition,
systemctl/units-jobs-plain, andsystemctl/units-jobsreads only
the indented header.ip addressoutput whose firstinetline is past the first twenty
lines, as when nine or more interfaces without an address come first,
was read asip linkwith each address and lifetime as a setting.
iproute2's output is now read asip address(its header has the
group and no mode), and BusyBox's, whose header is the same for both,
is refused.sysctl/freebsdno longer reads the lines of a value printed over
...
v0.1.0
The first release of jsonize and its jz command.
Added
jzconverts command output from standard input or--fileinto
JSON. It identifies the format from the text, and refuses input it
cannot identify or read completely instead of guessing.jz run COMMAND [args...]runs a command without a shell, converts its
standard output and exits with the command's status. The command name
and its arguments narrow the choice of definition;--env,
--keep-localeand--timeoutcontrol how the command runs.- Output options:
--pretty,--yaml,--streamfor commands that keep
printing,--extractand--excludefor keys, and--rawfor values
without field conversion. - Selection options:
--parserand--variantname a definition,
--definesupplies one inline,--columnsnames csv columns, and
--explainreports why a definition was chosen. --assume-yearand--assume-zonefor timestamps printed without a
year or with a zone abbreviation.- An embedded registry of 451 YAML definitions for 169 commands and
files, covering GNU, BusyBox, macOS, FreeBSD and Windows variants where
their output differs, with 1,226 captured or documented fixtures. - A published JSON Schema for the output of every definition under
registry/schemas, shown byjz list --schema COMMAND VARIANT. jz listfor supported commands and definitions, andjz testfor
checking local definitions against their fixtures and the official
fixture corpus.- Layered registries:
JSONIZE_REGISTRY_PATH, the user registry and the
embedded registry. Definitions are data and are never downloaded. - bash and zsh completion from
jz completion. - Go packages under
pkg/for loading registries, selecting a definition
and parsing text. - Release archives for Linux, macOS and Windows on amd64 and arm64,
.deb,.rpmand.apkpackages, SHA-256 checksums and GitHub build
provenance.
Known limitations
- Rounded human-readable sizes such as
1.8Tstay strings. - Some generic formats, such as
du,wcandenv, are read only when
the parser is named. - Windows command output is read for
ipconfig,ipconfig /alland
systeminfoin English. Other languages and code pages are not
checked. - FreeBSD is tested by the end-to-end suite but has no release archive.
Install it withgo install. - Windows release binaries are built with
GOEXPERIMENT=nogreenteagc,
the garbage collector configuration the Windows CI jobs run.