Changes in version 0.1.2 - A self_contained flag on both writers carries an S7 class definition even where a package would find the class again, for a document meant as an archive rather than a wire format. See vignette("design") for what it leaves referenced (#65). - The self_contained flag carries an R6 class generator as well, recording what the generator binds apart from R6's own machinery and putting that back into an empty generator on the way in, so a document holding one reads where the class is gone. A generator the default refuses for want of a name that finds it again is carried rather than refused (#69). - The self_contained flag writes an R6 instance whole, as the environment it is, without asking its class for a json_state() method, so a document holding one reads where neither the class nor R6 is available. Everything the instance holds is written, a field a method would leave out included, and a handle it holds stops the write (#73). - A function definition inside a body no longer carries the file it was parsed from into the document. The parser keeps that source reference as part of the function call rather than as an attribute, where the writer did not look for one (#98). - Reading a document no longer runs a call recorded with the S4 bit or an S7_class attribute. Either one sends the value to the class's validity check, which, unlike every other hook, was handed it outside quote(), so R evaluated the call on the way in (#96). - The S4 bit comes back on every type the reader builds. Complex, raw, language, pairlist, environment and function values used to lose it, a method definition among them, and with the bit went the class's validity check, which runs only on a value carrying it, so an edited document could read into a value its class rejects. A document asking for the bit on an object R shares, which is NULL, a symbol, a primitive or an environment recorded by name, no longer sets it on that object for the rest of the session (#96). - An active binding is recorded by the function it runs rather than refused, and comes back active, so an environment holding one round-trips, as does an R6 instance with an active field under the self_contained flag. The function is called on neither side, which is how serialize() records one too (#99). - The self_contained flag asks no json_state() method a class author wrote, for any class rather than for an R6 instance alone, since the record one writes is read back through a json_revive() method found by name. A value whose class has such a method is written by the rule for its type, as serialize() writes every object, so a field the method leaves out is written and a handle it stands in for stops the write. The package's own methods still run, so an S7 class and an R6 generator are carried, and a reference class instance is refused whatever methods its class has (#101). - A document carrying attributes on an environment recorded by name, such as the global environment or a namespace, is refused, as one carrying attributes on NULL or on a primitive already was. The reader used to set them on the environment the name finds, where they stayed for the rest of the session (#103). - A document R refuses partway through the read, such as one whose dim does not fit its value, one asking for a coercion R will not make or one holding a NUL in a string, is let go of along with every value it had numbered by then. Both used to stay in memory for the rest of the session, where a document the reader refuses itself never did (#110). - Reading a long name no longer understates R's count of vector memory. A name of 128 bytes or more that R already held under a copy with another encoding mark, as it holds a non-ASCII symbol the parser has read, lowered the count on every read by an amount taken from the name's hash rather than its length, wrapping it past zero in a session holding less, and a name over R's 10,000-byte limit did so on its way to being refused. A symbol the reader creates from a non-ASCII name of 128 bytes or more now has a name without an encoding mark, as one the parser creates already has (#116). Changes in version 0.1.1 (2026-09-24) - Installs cleanly with GCC 16 under link-time optimization, where 0.1.0 drew a spurious -Wstringop-overflow warning (#86). Changes in version 0.1.0 (2026-09-18) First release, carrying the format and the two round-trip contracts described in vignette("design"). - Reading and writing through json_write(), json_read(), json_write_str() and json_read_str(). - Ordinary JSON for attribute-free vectors and lists, with the integer-versus-double distinction carried by the number lexeme. - Prefix-tagged strings for typed NA, Inf, -Inf and NaN, with an ordinary string starting with the prefix escaped by doubling it. - Text is carried as UTF-8 whatever the locale, so a value writes the same document on every machine. Bytes that are not valid UTF-8 are refused, naming the path (#37). - A tagged ~a / ~v object carries anything with attributes, covering Date, POSIXct, factors, matrices, data frames and classed lists through one rule. Attributes come back in document order (#67). - Objects from S3, S4, S7 and R6. An S4 or S7 value is rebuilt rather than constructed and then validated, so a custom initialize or constructor does not run and the slots it would derive come back as the document spells them (#55). - An S7 class that no name can find again carries its definition rather than a reference, so a class defined outside a package round-trips into another session (#64). - An R6 instance is refused unless its class supplies a json_state() method, since only the class can say what its state is. See vignette("r6") (#44). - A reference class instance and its generator are refused on the same grounds. An environment you have classed yourself is not, the environment rule applying to it unchanged (#57). - The r6_state() and r6_restore() pair records an R6 instance as its public and private bindings, for a class whose bindings genuinely are its state. Rebuilding skips initialize (#44). - Language objects round-trip exactly. Calls, expressions and pairlists are written as their elements and symbols as ~:name, with nothing deparsed, so a call keeps any R object in its tree (#19). - Closures round-trip through their formals, body and environment, exactly wherever that environment is recorded by name. No source reference is kept (#20). - Environments round-trip up to equivalence, as base::serialize() does: the global, base, empty, namespace, package and imports environments by name, anything else by what it binds. Promises and active bindings are refused, since reading either runs code (#18). - An extension protocol, json_state() and json_revive(), for classes the default rule does not fit. A method has no privileges, whatever it returns being written under the same rules as any other value (#56, #61). - Reference identity is preserved across a document. A repeated environment or R6 instance is written once under a ~id and referenced after, so shared state and cycles come back shared (#21). - Plain JSON for a consumer that brings its own schema, through typed = FALSE on both writers. Annotations are dropped and a length-one vector is a scalar unless it is AsIs. Values JSON cannot express are refused rather than written as null (#47, #52). - Files are written straight from the document rather than through R, which roughly halves peak memory on a large write. A refused write leaves an existing file untouched (#50). - Builds on R 4.3 and later, the floor being declared in DESCRIPTION so an older R is refused while the install resolves rather than part way through compiling (#72).