API’s, open data & integratie

JSON-inname: weiger dubbelzinnige bytes vóór je het datamodel toetst

De kern

Een decoder die iets teruggeeft, bewijst nog geen eenduidige JSON-overdracht. Scheid bytebudget, representatie, decodering en domeincontrole.

Wat past bij jou? Beantwoord 30 korte vragen →

Voorkom verlies voordat je velden ziet

Stel dat een antwoord dezelfde sleutel tweemaal bevat. Als de decoder er stilzwijgend één bewaart, kan de latere schemacontrole niet meer zien dat er een conflict was. Een adapter heeft daarom een innamegrens nodig vóór de controle op verplichte velden. Die grens bepaalt welke aangeleverde bytes je überhaupt als een bruikbaar document beschouwt.

RFC 8259 beschrijft UTF-8 voor interoperabele uitwisseling, beveelt unieke objectnamen aan en laat NaN niet toe in de getalgrammatica.[2] De onderstaande lokale afspraak weigert dubbele namen in plaats van een winnaar te kiezen. Dat is bewust strenger dan een vrijblijvende waarschuwing; noem het een adapterbeleid, geen universeel gedrag van iedere JSON-parser.

Zet de controles in een vaste volgorde

  • Controleer het type en het budget van de reeds ontvangen bytes.
  • Laat alleen de afgesproken mediarepresentatie door.
  • Decodeer als UTF-8 zonder vervangende tekens te verzinnen.
  • Weiger dubbele namen en niet-JSON-constanten bij het parsen.
  • Pas daarna het resourceschema toe; die laatste stap is hier niet gebouwd.

De uitgevoerde bytegevallen

De geldige letterlijke invoer is {"n":1.00,"enabled":true}. Decimal bewaart in deze proef de tekstuele decimaalvorm 1.00; enabled blijft een boolean. De limiet van 4096 bytes en de keuze voor application/json zijn lokale oefenafspraken. Andere mediatypen worden niet automatisch geaccepteerd omdat hun naam op json eindigt.

Gebruik Kopieer code om alle code exact te kopiëren. Handmatig selecteren kan de laatste regelovergang weglaten.

import json
from decimal import Decimal

def unique(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError("duplicate member")
        result[key] = value
    return result

def no_constant(value):
    raise ValueError("non-JSON constant")

def intake(raw, media_type):
    if type(raw) is not bytes or len(raw) not in range(4097):
        raise ValueError("byte budget or type")
    if media_type.split(";", 1)[0].strip().lower() != "application/json":
        raise ValueError("unsupported representation")
    return json.loads(raw.decode("utf-8"), object_pairs_hook=unique,
                      parse_float=Decimal, parse_constant=no_constant)

# Synthetic wire bytes, not captured provider traffic.
good = intake(b'{"n":1.00,"enabled":true}', "application/json")
assert type(good["n"]) is Decimal and str(good["n"]) == "1.00"
assert type(good["enabled"]) is bool and good["enabled"] is True
bad = [b'{"a":1,"a":2}', b'{"nested":{"x":1,"x":2}}',
       b'{"n":NaN}', b'{"x":"\xff"}', b'{} {}', b' ' * 4097]
for raw in bad:
    try:
        intake(raw, "application/json")
    except (ValueError, UnicodeError):
        pass
    else:
        raise AssertionError("bad wire bytes accepted")
try:
    intake(b'{}', "text/html")
except ValueError:
    pass
else:
    raise AssertionError("HTML representation accepted")
print(json.dumps({"decimal_text": str(good["n"]), "boolean_type": type(good["enabled"]).__name__,
                  "wire_rejections": len(bad), "media_rejections": 1}, sort_keys=True))

{"boolean_type": "bool", "decimal_text": "1.00", "media_rejections": 1, "wire_rejections": 6}

De uitvoer telt zes geweigerde bytefixtures: een dubbele naam op het hoogste niveau, een geneste dubbele naam, NaN, een ongeldige UTF-8-byte, twee opeenvolgende documenten en overschrijding van het budget. Een afzonderlijke representatieproef weigert text/html. Dit zijn echt uitgevoerde lokale gevallen, geen onderschepte providerantwoorden of metingen van een API.

Geef de volgende laag geen schijnzekerheid

Een geaccepteerde boolean kan nog steeds verkeerd zijn voor een veld dat een aantal verwacht. Ook een syntactisch correct object met een ontbrekende sleutel blijft mogelijk onbruikbaar. De oefening doet geen vergelijking met een verwachte payload en gebruikt geen Python-dictionarygelijkheid om types te bewijzen. De twee expliciete typeasserts gelden alleen voor de gekozen geldige fixture.

De bytes zijn al volledig in geheugen: dit is geen geteste downloadlimiet, decompressiebeveiliging of streamingparser. Recursiediepte, extreem lange getallen, Unicode-normalisatie en eindige Decimal-berekeningen vragen aanvullende grenzen. Bewaar bij een fout liever een veilige foutcategorie en contractversie dan de ruwe antwoordinhoud. Brononderzoek en offline uitvoering zijn gereed; netwerkgedrag, rechten en onafhankelijke taal- en feitenreview blijven open.

Bronnen & onderbouwing

  1. Oorspronkelijke brontitel: RFC 8259 — The JavaScript Object Notation (JSON) Data Interchange Format

    Geraadpleegd: 2026-10-18T22:58:30.094971+00:00

Een keuzehulp voor jouw situatie

Je antwoorden blijven in deze pagina tot je ze zelf indient. Niet-opgeslagen antwoorden verdwijnen bij herladen.

Opslaan is nog niet ingesteld. Er worden geen antwoorden of contactgegevens verzonden of bewaard.

Welke integratiestap helpt je verder?

30 korte vragen over API’s en gegevens koppelen. Een antwoord kiezen gaat automatisch verder. Je kunt teruggaan, wijzigen en overslaan. Dit is een praktische leesroute, geen wetenschappelijke test.

JavaScript is nodig voor deze keuzehulp.

Contact & correcties

Stuur een vraag of meld een correctie via dit formulier.

Alleen na Verzenden worden je naam, e-mailadres, onderwerp en bericht naar de contactdienst gestuurd om je bericht te behandelen. Dit is geen nieuwsbriefinschrijving. Niet-verzonden tekst verdwijnt bij herladen. Privacyinformatie

Voorbeeld: verzenden is niet ingesteld. Dit formulier verzendt of bewaart niets.

Je bericht — alle velden zijn verplicht