4.2 JSON als uitwisselingsformaat
Kent JSON als modern uitwisselingsformaat (syntax, schema’s, voordelen ten opzichte van XML).
Wat is JSON?
JSON (JavaScript Object Notation) is een lichtgewicht, text-based data-uitwisselingsformaat. Hoewel oorspronkelijk ontworpen voor JavaScript, is JSON nu de de-facto standaard geworden voor moderne web-APIs en wordt steeds vaker ingezet in overheidscontext als alternatief voor XML.
Kerneigenschappen:
- Compact: Minder overhead dan XML
- Human-readable: Eenvoudig te lezen en schrijven
- Native JavaScript: Perfect voor web-applicaties
- Language-agnostic: Support in alle moderne programmeertalen
JSON-syntax en structuur
Basis JSON-datatypes
{
"string_waarde": "tekst",
"number_waarde": 42,
"decimal_waarde": 3.14159,
"boolean_waarde": true,
"null_waarde": null,
"array_waarde": [1, 2, 3, "vier"],
"object_waarde": {
"nested_key": "nested_value"
}
}
Persoon-voorbeeld (vergelijkbaar met XML)
{
"persoon": {
"bsn": "123456789",
"geslachtsnaam": {
"voorvoegsel": "van der",
"geslachtsnaamStam": "Berg"
},
"voornamen": "Jan Peter",
"geboortedatum": "1985-03-15",
"adres": {
"straatnaam": "Hoofdstraat",
"huisnummer": 42,
"huisnummertoevoeging": "A",
"postcode": "1234AB",
"woonplaatsnaam": "Amsterdam"
},
"contactgegevens": [
{
"soort": "email",
"waarde": "jan.vandenberg@example.com"
},
{
"soort": "telefoon",
"waarde": "06-12345678"
}
]
}
}
JSON vs XML-vergelijking
XML-versie:
<persoon>
<bsn>123456789</bsn>
<voornaam>Jan</voornaam>
<achternaam>Berg</achternaam>
</persoon>
JSON-versie:
{
"persoon": {
"bsn": "123456789",
"voornaam": "Jan",
"achternaam": "Berg"
}
}
Difference: 47% minder characters in JSON!
JSON Schema’s
JSON Schema definieert de structuur en validatie-regels voor JSON-documenten, vergelijkbaar met XSD voor XML.
Basis JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://overheid.nl/schemas/persoon.json",
"title": "Persoon",
"description": "Schema voor persoonsgegevens in overheidscontext",
"type": "object",
"required": ["bsn", "voornaam", "achternaam"],
"properties": {
"bsn": {
"type": "string",
"pattern": "^[0-9]{9}$",
"description": "Burgerservicenummer (9 cijfers)"
},
"voornaam": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"achternaam": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"geboortedatum": {
"type": "string",
"format": "date",
"description": "Geboortedatum in YYYY-MM-DD formaat"
},
"geslacht": {
"type": "string",
"enum": ["M", "V", "O"],
"description": "Geslacht: M=Man, V=Vrouw, O=Onbekend"
}
},
"additionalProperties": false
}
Geavanceerde Schema-features
Conditionale validatie:
{
"type": "object",
"properties": {
"leeftijd": {"type": "integer"},
"toestemming_nodig": {"type": "boolean"}
},
"if": {
"properties": {"leeftijd": {"minimum": 18}}
},
"then": {
"properties": {"toestemming_nodig": {"const": false}}
},
"else": {
"properties": {"toestemming_nodig": {"const": true}}
}
}
Array-validatie:
{
"type": "object",
"properties": {
"contactgegevens": {
"type": "array",
"items": {
"type": "object",
"required": ["soort", "waarde"],
"properties": {
"soort": {
"enum": ["email", "telefoon", "mobiel"]
},
"waarde": {
"type": "string",
"minLength": 1
}
}
},
"minItems": 1,
"maxItems": 10
}
}
}
JSON in API-context
RESTful API-voorbeeld
Request (POST /api/personen):
{
"persoon": {
"bsn": "987654321",
"voornaam": "Maria",
"achternaam": "Jansen",
"geboortedatum": "1990-12-25",
"adres": {
"straat": "Kerkstraat 15",
"postcode": "5678CD",
"plaats": "Utrecht"
}
}
}
Response (Success - HTTP 201):
{
"status": "success",
"data": {
"id": "pers_abc123",
"persoon": {
"bsn": "987654321",
"voornaam": "Maria",
"achternaam": "Jansen",
"geboortedatum": "1990-12-25",
"created_at": "2024-03-05T14:30:00Z",
"updated_at": "2024-03-05T14:30:00Z"
}
},
"links": {
"self": "/api/personen/pers_abc123",
"adres": "/api/personen/pers_abc123/adres"
}
}
Response (Error - HTTP 400):
{
"status": "error",
"error": {
"code": "VALIDATION_FAILED",
"message": "De verstrekte gegevens zijn niet geldig",
"details": [
{
"field": "bsn",
"error": "BSN moet uit exacte 9 cijfers bestaan"
},
{
"field": "geboortedatum",
"error": "Ongeldige datumnotatie. Gebruik YYYY-MM-DD"
}
]
},
"timestamp": "2024-03-05T14:30:00Z",
"request_id": "req_xyz789"
}
JSON-processing tools en libraries
Command-line tools
# jq - JSON query en transformation tool
cat persoon.json | jq '.persoon.voornaam'
# Output: "Jan"
# Pretty-print JSON
cat compact.json | jq '.'
# Filter arrays
cat personen.json | jq '.personen[] | select(.leeftijd > 18)'
# Transform data
cat input.json | jq '{naam: .voornaam + " " + .achternaam, bsn}'
Programming libraries
JavaScript/Node.js:
// Native parsing
const data = JSON.parse(jsonString);
const jsonString = JSON.stringify(object, null, 2);
// Validation met ajv
const Ajv = require('ajv');
const ajv = new Ajv();
const validate = ajv.compile(schema);
const valid = validate(data);
if (!valid) console.log(validate.errors);
Python:
import json
import jsonschema
# Parse/stringify
data = json.loads(json_string)
json_string = json.dumps(data, indent=2, ensure_ascii=False)
# Validation
schema = {...} # JSON Schema object
jsonschema.validate(instance=data, schema=schema)
Java:
// Jackson library
ObjectMapper mapper = new ObjectMapper();
Persoon persoon = mapper.readValue(json, Persoon.class);
String json = mapper.writeValueAsString(persoon);
// JSON Schema validation
JsonSchema schema = JsonSchemaFactory.getInstance().getSchema(schemaNode);
Set<ValidationMessage> errors = schema.validate(jsonNode);
C#:
// Newtonsoft.Json
var persoon = JsonConvert.DeserializeObject<Persoon>(json);
var json = JsonConvert.SerializeObject(persoon, Formatting.Indented);
// JSON.NET Schema validation
JSchema schema = JSchema.Parse(schemaJson);
bool valid = data.IsValid(schema, out IList<string> errors);
JSON Best practices voor overheid
API-design patterns
HAL (Hypertext Application Language):
{
"_links": {
"self": {"href": "/api/personen/123"},
"adres": {"href": "/api/personen/123/adres"},
"documenten": {
"href": "/api/personen/123/documenten",
"templated": true
}
},
"bsn": "123456789",
"voornaam": "Jan",
"achternaam": "Berg",
"_embedded": {
"adres": {
"_links": {"self": {"href": "/api/adressen/456"}},
"straat": "Hoofdstraat 1",
"postcode": "1234AB"
}
}
}
JSON:API specification:
{
"data": {
"type": "personen",
"id": "123",
"attributes": {
"voornaam": "Jan",
"achternaam": "Berg"
},
"relationships": {
"adres": {
"links": {
"related": "/api/personen/123/adres"
},
"data": {"type": "adressen", "id": "456"}
}
}
},
"included": [
{
"type": "adressen",
"id": "456",
"attributes": {
"straat": "Hoofdstraat 1",
"postcode": "1234AB"
}
}
]
}
Security-overwegingen
Input sanitization:
// NOOIT direct user-input in JSON opnemen zonder validation
{
"user_input": "<script>alert('xss')</script>", // DANGER!
"safe_input": "<script>alert('xss')</script>" // Escaped
}
JSON injection prevention:
// FOUT: String concatenation
const unsafeJson = '{"name": "' + userInput + '"}';
// CORRECT: Object construction + JSON.stringify
const safeObject = {name: userInput};
const safeJson = JSON.stringify(safeObject);
Performance-optimalisatie
Streaming voor grote datasets:
// JSONStream voor Node.js
const JSONStream = require('JSONStream');
fs.createReadStream('large-dataset.json')
.pipe(JSONStream.parse('data.*'))
.on('data', (record) => {
// Process individual records
});
Paginering patterns:
{
"data": [...],
"pagination": {
"current_page": 1,
"total_pages": 50,
"total_items": 1250,
"items_per_page": 25,
"links": {
"first": "/api/personen?page=1",
"prev": null,
"next": "/api/personen?page=2",
"last": "/api/personen?page=50"
}
}
}
JSON vs XML in overheidscontext
Wanneer JSON kiezen:
✅ Voordelen:
- Web-APIs: Native support in browsers/JavaScript
- Mobile apps: Minder data-usage, sneller parsing
- Microservices: Lightweight communication tussen services
- Documentation: Eenvoudiger te begrijpen voor developers
- Performance: 15-30% kleiner, 2-3x sneller parsing
✅ Use cases:
- Nieuwe API’s en webservices
- Real-time data-uitwisseling
- Integration met moderne front-end frameworks
- IoT en mobile-first applicaties
Wanneer XML behouden:
⚠️ XML blijft relevant voor:
- Legacy-systems: Bestaande StUF-implementaties
- Compliance: Wettelijke eisen die XML specificeren
- Complex metadata: Rich attribute-modellen nodig
- XSLT transformations: Bestaande processing pipelines
- Industry standards: Sectoren met XML-vereisten
Hybride-scenario’s
XML-to-JSON transformation:
// Express middleware voor backwards compatibility
app.use('/api/legacy', (req, res, next) => {
if (req.headers['content-type'] === 'application/xml') {
// Convert incoming XML to JSON
const jsonData = xml2json(req.body);
req.body = jsonData;
}
next();
});
Content-negotiation:
# Client vraagt JSON
curl -H "Accept: application/json" /api/personen/123
# Client vraagt XML
curl -H "Accept: application/xml" /api/personen/123
Toekomst van JSON in overheid
Emerging patterns
GraphQL:
// Query
{
persoon(bsn: "123456789") {
voornaam
achternaam
adres {
postcode
plaats
}
}
}
// Response
{
"data": {
"persoon": {
"voornaam": "Jan",
"achternaam": "Berg",
"adres": {
"postcode": "1234AB",
"plaats": "Amsterdam"
}
}
}
}
JSON-LD (Linked Data):
{
"@context": "https://overheid.nl/context/persoon",
"@type": "Persoon",
"bsn": "123456789",
"naam": "Jan Berg",
"woontIn": {
"@type": "Adres",
"postcode": "1234AB"
}
}
Migratie van XML naar JSON
Geleidelijke overgang
- Dual support: API’s die zowel XML als JSON accepteren
- Schema mapping: XSD naar JSON Schema conversie
- Testing: Parallel data-validation tijdens transitie
- Documentation: Clear migration-guides voor consumers
- Deprecation: Planned obsolescence van XML-endpoints
Het begrijpen van JSON is essentieel geworden voor moderne overheidsapplicaties. Terwijl XML zijn plaats behoudt in legacy-systemen, biedt JSON significante voordelen voor nieuwe ontwikkeling, vooral in web-context en API-ecosystemen.
Resources: