Een beschadigde JSON-sequentie lezen zonder volledigheid te veinzen
De kern
Herstelbaarheid en volledigheid zijn verschillende eigenschappen. Geef een beschadigd record een zichtbare plaats in het resultaat.
Herstel is een productbeslissing
Een client die meerdere JSON-records ontvangt, moet eerst het afgesproken framingformaat kennen. Een JSON-array, losse documenten per regel en een JSON-tekstsequentie zijn geen onderling verwisselbare bestanden. De vraag hier is hoe je bij een sequentie de bruikbare delen kunt onderzoeken zonder een beschadiging onzichtbaar te maken.
RFC 7464 beschrijft UTF-8 JSON-tekstsequenties en herstel na beschadigde elementen; opeenvolgende RS-bytes betekenen geen lege elementen.[5] De oefening negeert lege stukken tussen separators, ook na een afsluitende RS. Het lokale acceptatiebeleid vereist LF na iedere niet-lege kandidaat en een object met tekstuele id. Dat objectprofiel en die LF-eis vormen niet de volledige parsergrammatica.
Maak een lokale parsefout zichtbaar
- accepted_ids bevat alleen de objecten die dit lokale profiel doorstaan.
- gaps bevat lokale rangnummers van niet-lege parsekandidaten, geen betrouwbare bronrecordposities.
- partial_hold verbiedt in deze oefening de claim dat de export volledig is.
- framing_ok betekent uitsluitend dat dit framingprofiel geen gat vond; het bewijst geen verwachte recordset.
De letterlijke beschadigde invoer uitvoeren
De bytefixture hieronder zet een RS vóór ieder element. Tussen de intacte objecten met id A en C staat een onvolledig object. De code ontvangt de volledige bytebuffer, maximaal 4096 bytes, en verstuurt niets. Het doel is een zichtbare gatadministratie, niet een bestand repareren door verzonnen inhoud toe te voegen.
Gebruik Kopieer code om alle code exact te kopiëren. Handmatig selecteren kan de laatste regelovergang weglaten.
import json
def recover(raw):
if type(raw) is not bytes or len(raw) not in range(4097) or (raw and not raw.startswith(b"\x1e")):
raise ValueError("unsupported sequence envelope")
accepted, gaps = [], []
# Consecutive/trailing RS bytes do not create empty sequence elements.
candidates = (chunk for chunk in raw.split(b"\x1e")[1:] if chunk)
for number, chunk in enumerate(candidates, 1):
try:
if not chunk.endswith(b"\n"):
raise ValueError("missing final LF under local policy")
value = json.loads(chunk.decode("utf-8"))
if type(value) is not dict or type(value.get("id")) is not str:
raise ValueError("this example requires an object with text id")
except (ValueError, UnicodeError):
gaps.append(number)
else:
accepted.append(value["id"])
return {"accepted_ids": accepted, "gaps": gaps,
"state": "partial_hold" if gaps else "framing_ok"}
# Literal RS bytes surround one broken object and two intact objects.
fixture = b'\x1e{"id":"A"}\n\x1e{"id":\n\x1e{"id":"C"}\n'
result = recover(fixture)
assert result["accepted_ids"] == ["A", "C"] and result["gaps"] == [2]
assert recover(b'\x1e{"id":"A"}')["gaps"] == [1]
assert recover(b'\x1etrue\n')["gaps"] == [1]
assert recover(b'\x1e{"id":1}\n')["gaps"] == [1]
for bad in [b'{"id":"A"}\n', b' ' * 4097]:
try:
recover(bad)
except ValueError:
pass
else:
raise AssertionError("unsupported envelope accepted")
assert recover(b'\x1e\x1e{"id":"A"}\n\x1e')["gaps"] == []
assert recover(b'\x1e\x1e')["gaps"] == []
print(json.dumps({"recovery": result, "additional_gap_cases": 3, "envelope_rejections": 2,
"separator_run_cases": 2}, sort_keys=True))
{"additional_gap_cases": 3, "envelope_rejections": 2, "recovery": {"accepted_ids": ["A", "C"], "gaps": [2], "state": "partial_hold"}, "separator_run_cases": 2}
De echte lokale uitvoer bewaart A en C en meldt lokale kandidaatpositie 2. Dit is geen betrouwbaar bronrecordnummer: bytes kunnen ontbreken, extra RS kan kandidaten splitsen en separatorreeksen tellen niet mee. Aanvullende asserts houden een object zonder laatste LF, een boolean en een numerieke id tegen. Twee envelopfixtures weigeren ontbrekende initiële RS en een te grote buffer. Twee separatorfixtures tonen dat herhaalde en afsluitende RS geen gaten uitvinden. Er is geen netwerkonderbreking gesimuleerd; alleen vooraf samengestelde bytes zijn verwerkt.
Kies apart wat gedeeltelijk bruikbaar is
Een inspectievenster kan intacte records met een opvallende gedeeltelijk-melding tonen. Een consumer die een volledige export nodig heeft, moet het geheel vasthouden en de overeengekomen herstelroute volgen. Dezelfde parse-uitkomst kan dus tot verschillende productacties leiden. Verwijder de gaten niet om een succesvolle teller te krijgen en noem geaccepteerde objecten geen volledige bronkopie.
Dit is geen streamingimplementatie: chunkgrenzen, backpressure en tussentijdse opslag zijn niet getest. Dubbele JSON-namen, dubbele id’s, toepassingsschema’s en niet-eindige getallen vragen afzonderlijke controles. Het voorbeeld voert geen import, retry of write uit en bewijst geen recordvolledigheid. Brononderzoek en synthetische lokale uitvoering zijn gedaan; toegang, rechten en onafhankelijke feiten- en vertaalreview zijn geen onderdeel van dit resultaat.
Bronnen & onderbouwing
- Oorspronkelijke brontitel: RFC 7464 — JavaScript Object Notation (JSON) Text Sequences
Geraadpleegd: 2026-10-18T22:58:31.189040+00:00