J++Jsonic++
Contracts

Correctness is expressed as behavior that must remain true.

Jsonic++ treats parser rules as contracts: valid JSON should parse consistently, invalid JSON should fail deterministically, errors should remain useful, and serialization should preserve the represented value.

Contract-first development: define the guarantee, prove that it can be checked simply, add executable evidence, then change the parser.

Syntax contracts

  • Objects require quoted string keys and a colon between each key and value.
  • Arrays and objects reject trailing commas.
  • Only JSON whitespace is ignored between tokens.
  • Literals are exactly true, false and null.
  • Unexpected data after the root value is rejected.

Object contracts

  • Duplicate object keys are rejected during ordinary parsing and the named-array streaming path.
  • Object member insertion order is preserved in the represented document and therefore in serialization.
  • has() reports membership only for object values.
  • Const indexing never invents a missing member; it throws std::out_of_range.
  • Mutable indexing may create a missing null member, but indexing a non-object value is an error.

Number and string contracts

Number handling validates JSON syntax before converting to the library's double representation. Malformed exponents, leading-zero forms and values outside the accepted finite range are failures rather than best-effort coercions.

Strings validate escapes and Unicode surrogate pairs. Escaped UTF-16 surrogate pairs are combined into UTF-8; lone or malformed surrogates are rejected. Serialization applies JSON escaping again so represented strings cannot accidentally emit invalid quote/control syntax.

Error contracts

Ordinary parse errors return false and a message carrying line and column context. That means a configuration reader can report a useful location without catching an exception merely because input was invalid. Programmer misuse of the DOM API—such as array indexing on an object—is different and uses standard exceptions.

Round-trip contracts

For values representable by the model, serialization should produce valid JSON that parses back to the same logical value. Pretty and compact modes may differ in whitespace, but not in represented content. Round-trip fixtures are particularly useful because they exercise the parser and writer as a pair rather than proving each side only against hand-selected text.

Streaming contracts

for_each_array_item() is intentionally narrow: the root must be an object, the requested member must exist and must be an array, and every item is parsed as an ordinary Document before the callback sees it. Other root members are validated and skipped. A callback that returns false stops processing and reports array item rejected.

Synchronization contracts

The copies consumed by Nift and Minify++ are expected to remain byte-identical where the integration model requires it. A parser change is therefore incomplete until standalone tests and downstream integration checks agree. That turns vendoring from “copy this file and hope” into an explicit maintenance relationship.

Why contracts matter

A parser can appear correct across hundreds of ordinary fixtures while still accepting one dangerous malformed family. The suite therefore needs both positive examples and adversarial neighbors designed to falsify each guarantee. See Battle tested for how those contracts are exercised.