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, significant numeric spellings should not be silently destroyed, and serialization should preserve the represented value.
Syntax contracts
- Objects require quoted string keys and a colon between each key and value.
- Arrays and objects reject trailing commas by default.
- Only JSON whitespace is ignored by default.
- Comments and trailing commas are independent explicit options; neither enables other JavaScript syntax.
- Literals are exactly
true,falseandnull. - Unexpected data after the root value is rejected.
Object contracts
- Ordinary DOM parsing preserves duplicate object members in source order; applications can explicitly request duplicate rejection.
- The named-array streaming path rejects duplicate root keys because it cannot represent them as a complete object.
- 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 conversion. Ordinary values use Type::Number with a double payload; spellings whose significant representation would be lost use Type::StrNumber and retain the original token in .string while still exposing the converted .num. Both are numbers to callers through is_number(). Malformed exponents and leading-zero forms 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. Callers may use the compatibility error string or a structured diagnostic carrying a message, zero-based byte offset and one-based line and column. 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
Serialization should produce valid JSON that parses back to the same represented value. Pretty and compact modes may differ in whitespace, but not in represented content; preserved StrNumber tokens are part of that contract. 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 independent conformance corpus therefore sits above local examples, and performance work sits behind that conformance gate. See Battle tested for the retained evidence.