Replace, create, remove
#include "fson/Number.hh"
#include "fson/String.hh"
// replace a value (or append the member if the path's last key is missing)
doc.setPath( "database.host", std::make_unique<String>( "db1" ) );
// create missing intermediate OBJECTS in one call
doc.setPath( "database.pool.size", std::make_unique<Number>( 8LL ) );
// remove an active member, or erase an array element
doc.removePath( "retries[1]" );
setPath():
- never grows arrays and never creates them — an index in the path must already exist;
- never resurrects a disabled member — a new active member is appended beside it (consistent with the query semantics);
- fails without side effects — any intermediate objects it created during a walk that then failed are rolled back.
Switch members off and on
doc.getRoot().disable( "retries" ); // active → disabled
doc.getRoot().at( "database" ).asObject()->enable( "user" ); // first disabled → active
The data is kept either way — disable() / enable() only flip the flag. enable() fails if an active member with that name already exists.
Save
if( !Fson::save( doc, "config.fson" ) )
std::cerr << "cannot write config.fson\n";
Or drive FsonWriter directly for a stream, a string, or a non-default dialect.
The round-trip guarantee
The writer pretty-prints — 2-space indent, one member/element per line, key: value with a single space after : — and writes back everything the parser captured:
| Preserved exactly | Normalised | |
|---|---|---|
| comments and their positions (header, leading, trailing, dangling, footer; nested blocks) | indentation, blank-line runs | |
| member and element order | — | |
quoted-vs-bare key form (unless preferBareKeys) | — | |
-- disabled markers | — | |
number lexemes — 1.50 stays 1.50, 0xFF stays 0xFF, Infinity stays Infinity | — | |
| `\ | `-block string form | the block is re-emitted at the value's indentation |
Output is a fixed point: writing an already-written document changes nothing (write(parse(write(x))) == write(parse(x))), and a modify-then-write cycle keeps every unrelated comment.
Empty containers stay compact ({} / []) — unless they carry dangling comments, which would otherwise be lost, so the expanded form is forced.
Strings are re-emitted double-quoted, escaping only the mandatory set; raw UTF-8 (including decoded \uXXXX) passes through — "ü" becomes "ü".

