Skip to content
Guides

4 JSON5 to JSON Checks Before API Release

·9 min read
4 JSON5 to JSON Checks Before API Release - Guides guide from JSON Prettifier

Quick answer: Check JSON5-to-JSON conversion at four boundaries: source grammar, non-finite numbers, duplicate members and downstream application acceptance. Parse with the declared source format, serialize with a strict output policy, and test the resulting values. A successful conversion should not conceal rejected ambiguity or silently change data to make it parseable.

A hand-edited configuration works locally, but its exported API payload fails. Removing comments may be necessary; it is not the whole conversion contract. The source can also contain number values and member ambiguities that need a deliberate policy.

These examples were executed with Python 3.12.14 and the Python json5 package 0.17.3. They use an explicit rejection policy for duplicates and non-finite numbers. Our comments-in-JSON guide explains format choices, while the JSON configuration checklist covers the broader deployment workflow.

1. Does the source parser accept only the declared input format?

Use a JSON5 parser for JSON5 input and a strict JSON encoder for the exported document, then compare the decoded result with an approved fixture. Include relaxed syntax and a string containing comment-looking characters. A comment-removal shortcut must not damage ordinary string data or be mistaken for a complete source-language parser.

The JSON5 project describes the format as useful for hand-maintained data and configuration. Its foundational March 2018 specification defines the accepted grammar. JSON5 is not arbitrary JavaScript: selecting a parser should not become permission to execute a configuration file as code.

import json
import json5

source = """
{
  // notes stay in the editable source
  name: 'Maya',
  path: 'https://example.com/a//b',
  count: 0x10,
  ratio: .5,
  tags: ['new',],
}
"""
value = json5.loads(source, allow_duplicate_keys=False)
output = json.dumps(value, allow_nan=False, ensure_ascii=False)
assert json.loads(output) == {
    'name': 'Maya', 'path': 'https://example.com/a//b',
    'count': 16, 'ratio': 0.5, 'tags': ['new'],
}
assert json.loads(output)['path'].endswith('/a//b')

The hexadecimal spelling becomes a number in the decoded model. The comments and original quote choices are source representation, not fields in the resulting value. Keep the original file when comments, review notes or formatting are important to maintainers; a generated strict-JSON artifact serves a different purpose.

Do not validate conversion by counting removed comment markers. A URL can legitimately contain double slashes, and a string can contain asterisk-slash text without ending a comment in its own context. A real parser understands those contexts. Keep such strings in the regression corpus so a later optimization cannot replace parsing with a destructive text substitution unnoticed.

If a tool accepts JSONC rather than JSON5, test that exact dialect separately. Similar names do not imply identical accepted syntax, and permissive editor highlighting does not identify the parser running in production. Document the source extension, parser package and conversion entry point together so another team can reproduce the build artifact.

Include a deliberately invalid source alongside the relaxed-syntax positive fixture. A truncated object should fail rather than be repaired automatically by an unrelated convenience layer. If the application offers repair as an interactive aid, keep that workflow separate from unattended publication and require review of the repaired value. Acceptance tests should establish which source was actually approved, not merely that some transformation eventually produced a document.

Use the JSON Viewer on the generated strict-JSON document to inspect nested values. The viewer does not certify JSON5 grammar coverage or execute the rejection policies in these examples. Keep that distinction visible when reporting a conversion as verified rather than merely displaying a plausible result.

2. Are non-finite numbers rejected before strict output is published?

Test Infinity, negative Infinity and NaN at both the root and nested locations. These JSON5 number values cannot be emitted as standard JSON numbers. Configure the output encoder to reject them under a finite-number contract, and include ordinary finite values and explicit null as controls so the rejection policy does not expand accidentally.

Python's JSON encoder documentation describes the allow_nan option. The examples explicitly disable non-standard numeric output. This is necessary because relying on a serializer's default behavior is weaker evidence than asserting the strictness policy used to create the published artifact.

for text in ['Infinity', '-Infinity', 'NaN', '{limit: Infinity}', '[NaN]']:
    parsed = json5.loads(text, allow_duplicate_keys=False)
    try:
        json.dumps(parsed, allow_nan=False)
    except ValueError:
        pass
    else:
        raise AssertionError('non-finite value exported')

for text in ['{limit: 10}', '{limit: null}', '[0, .5, -2]']:
    parsed = json5.loads(text, allow_duplicate_keys=False)
    assert json.loads(json.dumps(parsed, allow_nan=False)) == parsed

Keep null separate from a rejected number. A receiver may interpret null as no limit, not configured or explicitly cleared. Replacing Infinity with null can therefore change behavior while producing valid JSON. If a product intentionally chooses such a mapping, document it, review it with the consumer and test the resulting operation rather than calling it a lossless conversion.

Check generated values too. An application may construct a non-finite float after source parsing, for example through a calculation or a library transformation. Place the strict output check at the final serialization boundary so the policy covers the value actually published, not only the text originally read from disk.

Include an error-path test that leaves the previous approved artifact intact. A conversion failure should not overwrite a deployment file with partial output or an empty fallback. Write to a controlled intermediate artifact and publish only after conversion and downstream checks succeed. Test that workflow through the application's file or upload adapter when it matters operationally.

Do not infer precision guarantees from the finite-number check. Finite values can still exceed a downstream runtime's exact integer range. Our JSON number precision guide covers that separate boundary. An identifier that requires exact digits may need a string contract rather than a permissive numeric conversion.

For deployments with multiple consumers, run the generated artifact through each supported consumer's parser and type checks. A Python encoder accepting an integer does not prove a JavaScript client preserves its digits. Keep a string identifier fixture and a numeric measurement fixture separate so a fix for exact identifiers does not indiscriminately turn every number into text or change the API's arithmetic fields.

3. Are duplicate member names detected before a dictionary loses them?

Reject duplicate member names during JSON5 parsing when the contract requires unambiguous objects. Test repeated names with both quoted and unquoted spellings and include a nested duplicate. Checking the final dictionary is too late if parsing already selected one value, because the exported JSON can look clean while concealing discarded source information.

The Python parser's project documentation describes its duplicate-member control. This guide uses allow_duplicate_keys set to false. That is an application acceptance choice using the named implementation, not a claim that every JSON5 library exposes the same option or reports the same diagnostics.

for text in [
    '{mode: 1, mode: 2}',
    '{mode: 1, "mode": 2}',
    '{nested: {limit: 1, limit: 2}}',
]:
    try:
        json5.loads(text, allow_duplicate_keys=False)
    except ValueError:
        pass
    else:
        raise AssertionError('duplicate member accepted')

assert json5.loads('{mode: 1, nested: {mode: 2}}',
                   allow_duplicate_keys=False) == {'mode': 1, 'nested': {'mode': 2}}

The accepted control is important: the same name appearing in different objects is not a duplicate within either object. A global text search for repeated words would reject legitimate data. The test should exercise object membership at the correct scope rather than treating matching source tokens as sufficient evidence of ambiguity.

Our duplicate JSON key guide explains the related interoperability issue. Conversion adds a practical consequence: once repeated members are collapsed, comparing the final strict document cannot prove that the source was unambiguous. Retain parse-time diagnostics and the sanitized source fixture used to reproduce the failure.

Test the actual loader configuration in the build or request path. A unit test calling the parser with rejection enabled cannot protect a production helper that omits the option. Wrap the configured parser in one named application function where practical, then route both tests and conversion jobs through it.

Avoid reporting line numbers or diagnostic text that the test has not verified. For user-facing errors, check whether the library exposes stable location data and map it deliberately. A short acceptance test can establish rejection without claiming that every malformed input receives a precise or identical error message across package upgrades.

Keep duplicate-policy changes visible in release review. A previous converter may have accepted a repeated member and selected a value that users now depend on. Tightening the policy can be the right decision, but it needs a migration or correction path for existing sources. Inventory representative configuration files and provide actionable rejection feedback instead of silently changing which repeated value survives.

4. Does the converted value satisfy the receiving API before side effects?

Validate the converted value against the endpoint's shape and value rules before publication or execution. Strict JSON syntax permits many documents that a particular API should reject. Test wrong root types, missing and extra fields, incorrect scalar types and accepted boundary values, then verify only approved values reach the protected operation.

The helper below accepts one small request shape and returns strict JSON text. Its limit must be an integer from one through twenty; a numeric string and a boolean are deliberately rejected. Those are the example's domain rules, not universal requirements for every JSON5 conversion pipeline.

def convert_request(text):
    data = json5.loads(text, allow_duplicate_keys=False)
    if not isinstance(data, dict) or set(data) != {'query', 'limit'}:
        raise ValueError('unexpected request shape')
    if not isinstance(data['query'], str) or not data['query']:
        raise ValueError('nonempty query required')
    if type(data['limit']) is not int or not 1 <= data['limit'] <= 20:
        raise ValueError('limit outside contract')
    return json.dumps(data, allow_nan=False, ensure_ascii=False)

assert json.loads(convert_request("{query:'books', limit:20,}")) == {
    'query': 'books', 'limit': 20,
}
for text in ['[]', "{query:'books'}", "{query:'books',limit:true}",
             "{query:'books',limit:'2'}", "{query:'books',limit:21}",
             "{query:'books',limit:2,admin:true}"]:
    try:
        convert_request(text)
    except ValueError:
        pass
    else:
        raise AssertionError('invalid API request converted')

Use an approved expected document when reviewing a change to the conversion helper. A structural comparison can reveal a string becoming a number or an extra field appearing, but it cannot decide whether that change is allowed by the business contract. Keep executable acceptance assertions and consumer review together rather than replacing one with the other.

Put rejected fixtures through the real publishing gate, with a synthetic operation counter or controlled output destination. Confirm that invalid input causes no upload, file replacement or downstream call. A helper raising an exception is only useful if its caller respects that result and does not silently continue with defaults.

Add size and nesting limits appropriate to the deployed parser and consumer. The local examples are intentionally small and do not establish memory safety for arbitrary untrusted documents. Test configured resource boundaries separately, and preserve clear failure reporting so a rejected oversized file is not mistaken for a successful empty configuration.

Keep the final artifact check independent from the source acceptance check. Store an approved expected object and compare it with the decoded generated file after writing or uploading, rather than only comparing an in-memory intermediate. This catches a publishing adapter that writes a stale buffer, adds another string layer or sends the source file instead of the generated output.

Wikipedia's JSON background supplies context on related formats. These four checks establish focused evidence for the tested parser, output settings and application policy. They do not certify every JSON5 implementation or guarantee downstream compatibility. Keep the editable source, generated artifact and acceptance results together so a future release can reproduce the exact conversion decision.

Frequently Asked Questions

Is JSON5 valid input for a strict JSON API?
JSON5 accepts syntax that strict JSON does not, including comments and some alternative string, key and number spellings. A strict JSON API needs a deliberately converted JSON document unless it explicitly supports JSON5 itself. Renaming a file or changing its media type does not transform its contents into strict JSON.
Can comments survive conversion from JSON5 to JSON?
Ordinary JSON has no comment syntax, so a value-based JSON5-to-JSON conversion does not preserve source comments as comments. Keep the editable source separately when those notes matter. Moving a comment into a new data field changes the document's shape and requires an explicit contract decision rather than being a lossless formatting operation.
What should conversion do with Infinity and NaN?
Infinity and NaN have no standard JSON numeric representation. A converter must reject them or apply a separately documented transformation. For an API that requires finite numbers, reject these values before publication and test nested cases. Producing parseable output after replacing a number with null does not prove its meaning was preserved.
Should duplicate keys be checked before conversion?
Detect duplicate object members during source parsing when your policy rejects them. Once a parser has collapsed repeated names into a dictionary, serializing that dictionary cannot reconstruct the discarded ambiguity. Include both quoted and unquoted spellings of the same member name in fixtures, and verify the selected parser's duplicate-handling option.
Does valid converted JSON prove that an API will accept it?
A syntactically valid JSON document can still have the wrong root type, missing fields, extra fields or unacceptable values. Validate the converted value against the receiving endpoint's contract and test the actual request path. A formatter or successful conversion supplies syntax evidence, not complete application compatibility or authorization.

Working with JSON right now?

Paste it into JSON Prettifier to format, validate, or minify it. Everything runs in your browser.

Open JSON Prettifier