Guide · Writing

Publish the whole document or nothing.

The writer validates a JSON call sequence and stages every output octet in a caller-defined destination transaction.

Define a whole-document destination

Instantiate Flyology_JSON.Writing with four destination operations. Begin starts unpublished staging. Write accepts all bytes, a prefix, or none. Commit is the only publication point. Abort ends an unpublished transaction.

The destination outlives the writer and must not retain the array passed to Write. Each formal operation is synchronous, finite, nonreentrant, and nonraising.

Begin
Stage
Commit
Publish

Freeze the profile before staging

Construct every field of Writer_Profile explicitly. Initialize validates and freezes the profile. It does not start a destination transaction.

Bind the writer to its destination and choose Maximum_Depth. The root object or array has depth one. A scalar root needs depth zero.

Write one balanced root value

Begin_Document opens the transaction. JSON calls then describe exactly one balanced root. Finish_Document validates the end state and requests commit.

   Writing.Initialize (Writer, Profile, Diagnostic);
   Require_Success (Diagnostic, "Initialize");

   Writing.Begin_Document (Writer, Diagnostic);
   Require_Success (Diagnostic, "Begin_Document");
   Writing.Begin_Object (Writer, Diagnostic);
   Require_Success (Diagnostic, "Begin_Object");
   Writing.Begin_Name (Writer, Diagnostic);
   Require_Success (Diagnostic, "Begin_Name");
   Writing.Put_Name_Fragment (Writer, Name, Diagnostic);
   Require_Success (Diagnostic, "Put_Name_Fragment");
   Writing.End_Name (Writer, Diagnostic);
   Require_Success (Diagnostic, "End_Name");
   Writing.Begin_String (Writer, Diagnostic);
   Require_Success (Diagnostic, "Begin_String");
   Writing.Put_String_Fragment (Writer, Value, Diagnostic);
   Require_Success (Diagnostic, "Put_String_Fragment");
   Writing.End_String (Writer, Diagnostic);
   Require_Success (Diagnostic, "End_String");
   Writing.End_Object (Writer, Diagnostic);
   Require_Success (Diagnostic, "End_Object");

   --  The complete candidate is still unpublished at this point.
   if Target.Published_Length /= 0 then
      raise Program_Error with "the destination published before commit";
   end if;

   Writing.Finish_Document (Writer, Diagnostic);
   Require_Success (Diagnostic, "Finish_Document");

The maintained example publishes {"message":"Hello, Ada!"} only after the final call succeeds. Run it with ./scripts/test-examples.sh.

Supply decoded text and exact numbers

Name and string fragments contain decoded UTF-8 octets. A caller can split them inside a UTF-8 sequence. The writer retains only the carry needed to validate the next scalar.

Number fragments contain an exact JSON number spelling. The writer validates the spelling across fragment boundaries and emits the admitted octets unchanged.

All fragment arrays can have arbitrary Ada bounds. Empty fragments are accepted no-ops in an active token.

Abort unpublished work after failure

A grammar, UTF-8, number, depth, destination, or commit failure publishes no document. The writer retains the primary diagnostic. Abort_Document cleans up an active unpublished transaction and preserves an earlier primary failure.

An asynchronous transfer can leave the writer interrupted. The owner must abort the document or leave the writer scope before any other use. If a destination formal violates its nonraising contract, unwind the writer scope immediately.

Ordinary compact output is not canonical

Ordinary_Compact adds no BOM or whitespace. It preserves caller member order and exact validated number lexemes. It leaves solidus and valid non-ASCII text unescaped.

The policy uses short standard escapes and uppercase hexadecimal for other escaped controls. It does not sort names, add a final line feed, or claim canonical JSON.