Erweiterte JSON-Strukturen und Design-Patterns
Meistern Sie erweiterte JSON-Techniken. Verschachtelte Strukturen, Referenzen, Polymorphismus und Design-Patterns für skalierbare JSON-Architekturen.
Big JSON Team
• Technical WriterExpert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.
# Erweiterte JSON-Strukturen und Design-Patterns
Von einfachen Key-Value-Paaren zu komplexen, skalierbaren JSON-Architekturen. Dieser Leitfaden zeigt fortgeschrittene Techniken und Patterns.
Verschachtelte Datenstrukturen
Tiefe Verschachtelung
{
"organization": {
"name": "Tech Corp",
"departments": [
{
"id": "dept-001",
"name": "Engineering",
"teams": [
{
"id": "team-001",
"name": "Backend",
"members": [
{
"id": "emp-001",
"name": "Max Mustermann",
"role": "Senior Developer",
"skills": ["Python", "Go", "Kubernetes"],
"projects": [
{
"id": "proj-001",
"name": "API Platform",
"status": "active",
"milestones": [
{
"id": "ms-001",
"title": "MVP Release",
"dueDate": "2026-03-01",
"completed": false
}
]
}
]
}
]
}
]
}
]
}
}
Flache vs. verschachtelte Strukturen
// Problem: Tiefe Verschachtelung
interface DeepNested {
level1: {
level2: {
level3: {
level4: {
value: string;
};
};
};
};
}
// Zugriff ist umständlich
const value = data.level1.level2.level3.level4.value;
// Lösung: Flachere Struktur mit IDs
interface Normalized {
entities: {
departments: Record<string, Department>;
teams: Record<string, Team>;
employees: Record<string, Employee>;
};
relationships: {
departmentTeams: Record<string, string[]>;
teamMembers: Record<string, string[]>;
};
}
Normalisierung und Denormalisierung
Normalisierte Struktur
{
"entities": {
"users": {
"user-1": {
"id": "user-1",
"name": "Alice",
"email": "alice@example.com"
},
"user-2": {
"id": "user-2",
"name": "Bob",
"email": "bob@example.com"
}
},
"posts": {
"post-1": {
"id": "post-1",
"title": "Erster Post",
"authorId": "user-1",
"commentIds": ["comment-1", "comment-2"]
}
},
"comments": {
"comment-1": {
"id": "comment-1",
"text": "Toller Post!",
"authorId": "user-2",
"postId": "post-1"
},
"comment-2": {
"id": "comment-2",
"text": "Danke!",
"authorId": "user-1",
"postId": "post-1"
}
}
}
}
Normalisierungs-Utility
interface Entity {
id: string;
[key: string]: any;
}
interface NormalizedData<T = any> {
entities: Record<string, Record<string, T>>;
result: string[];
}
function normalize<T extends Entity>(
data: T[],
entityName: string
): NormalizedData<T> {
const entities: Record<string, Record<string, T>> = {
[entityName]: {}
};
const result = data.map(item => {
entities[entityName][item.id] = item;
return item.id;
});
return { entities, result };
}
// Verwendung
const posts = [
{ id: 'post-1', title: 'Post 1', authorId: 'user-1' },
{ id: 'post-2', title: 'Post 2', authorId: 'user-2' }
];
const normalized = normalize(posts, 'posts');
console.log(normalized);
Referenzen und Verlinkungen
JSON Reference ($ref)
{
"definitions": {
"address": {
"type": "object",
"properties": {
"street": {"type": "string"},
"city": {"type": "string"},
"zipCode": {"type": "string"}
}
}
},
"user": {
"name": "Max Mustermann",
"homeAddress": {"$ref": "#/definitions/address"},
"workAddress": {"$ref": "#/definitions/address"}
}
}
Externe Referenzen
{
"product": {
"id": "prod-001",
"name": "Laptop",
"manufacturer": {
"$ref": "manufacturers.json#/entities/manufacturer-001"
},
"category": {
"$ref": "https://api.example.com/categories/electronics"
}
}
}
Referenzen auflösen
class JSONReferenceResolver {
private cache: Map<string, any> = new Map();
async resolve(data: any, basePath: string = ''): Promise<any> {
if (typeof data !== 'object' || data === null) {
return data;
}
if (Array.isArray(data)) {
return Promise.all(data.map(item => this.resolve(item, basePath)));
}
if (data.$ref) {
return this.resolveReference(data.$ref, basePath);
}
const resolved: any = {};
for (const [key, value] of Object.entries(data)) {
resolved[key] = await this.resolve(value, basePath);
}
return resolved;
}
private async resolveReference(ref: string, basePath: string): Promise<any> {
// Interne Referenz (#/path/to/data)
if (ref.startsWith('#/')) {
const path = ref.substring(2).split('/');
return this.getNestedValue(this.cache.get(basePath), path);
}
// Externe Referenz
if (ref.startsWith('http')) {
return this.fetchExternalReference(ref);
}
// Datei-Referenz
return this.loadFileReference(ref, basePath);
}
private getNestedValue(obj: any, path: string[]): any {
return path.reduce((acc, key) => acc?.[key], obj);
}
private async fetchExternalReference(url: string): Promise<any> {
if (this.cache.has(url)) {
return this.cache.get(url);
}
const response = await fetch(url);
const data = await response.json();
this.cache.set(url, data);
return data;
}
private async loadFileReference(ref: string, basePath: string): Promise<any> {
// Implementation für Datei-Laden
// ...
}
}
// Verwendung
const resolver = new JSONReferenceResolver();
const resolved = await resolver.resolve(jsonData);
Polymorphismus in JSON
Type Discriminator Pattern
{
"shapes": [
{
"type": "circle",
"radius": 5,
"centerX": 10,
"centerY": 20
},
{
"type": "rectangle",
"width": 10,
"height": 20,
"x": 5,
"y": 5
},
{
"type": "polygon",
"points": [
{"x": 0, "y": 0},
{"x": 10, "y": 0},
{"x": 5, "y": 10}
]
}
]
}
TypeScript Implementation
interface BaseShape {
type: string;
}
interface Circle extends BaseShape {
type: 'circle';
radius: number;
centerX: number;
centerY: number;
}
interface Rectangle extends BaseShape {
type: 'rectangle';
width: number;
height: number;
x: number;
y: number;
}
interface Polygon extends BaseShape {
type: 'polygon';
points: Array<{x: number; y: number}>;
}
type Shape = Circle | Rectangle | Polygon;
function calculateArea(shape: Shape): number {
switch (shape.type) {
case 'circle':
return Math.PI shape.radius 2;
case 'rectangle':
return shape.width shape.height;
case 'polygon':
// Polygon-Flächenberechnung
return calculatePolygonArea(shape.points);
default:
const _exhaustive: never = shape;
throw new Error(Unbekannter Shape-Typ: \\${_exhaustive});
}
}
Versionierung
Versionsnummer im Objekt
{
"version": "2.0",
"apiVersion": "v2",
"data": {
"user": {
"id": "user-123",
"profile": {
"name": "Max Mustermann",
"email": "max@example.com"
}
}
}
}
Schema-Versionierung
{
"$schema": "https://api.example.com/schemas/user/v2.json",
"id": "user-123",
"name": "Max Mustermann",
"email": "max@example.com",
"metadata": {
"schemaVersion": "2.0.0",
"migrated": true,
"previousVersion": "1.5.0"
}
}
Migrations-Handler
interface VersionedData {
version: string;
[key: string]: any;
}
type Migration = (data: any) => any;
class DataMigrationManager {
private migrations: Map<string, Migration> = new Map();
registerMigration(fromVersion: string, toVersion: string, migration: Migration) {
this.migrations.set(\\${fromVersion}->\\${toVersion}, migration);
}
migrate(data: VersionedData, targetVersion: string): any {
let currentVersion = data.version;
let currentData = { ...data };
while (currentVersion !== targetVersion) {
const nextVersion = this.getNextVersion(currentVersion, targetVersion);
const migrationKey = \\${currentVersion}->\\${nextVersion};
const migration = this.migrations.get(migrationKey);
if (!migration) {
throw new Error(Keine Migration von \\${currentVersion} zu \\${nextVersion});
}
currentData = migration(currentData);
currentVersion = nextVersion;
}
return currentData;
}
private getNextVersion(current: string, target: string): string {
// Logik zur Bestimmung der nächsten Version
// Vereinfachte Implementation
return target;
}
}
// Verwendung
const migrator = new DataMigrationManager();
migrator.registerMigration('1.0', '2.0', (data) => ({
version: '2.0',
user: {
id: data.userId,
profile: {
name: data.name,
email: data.email
}
}
}));
const oldData = { version: '1.0', userId: '123', name: 'Max', email: 'max@example.com' };
const newData = migrator.migrate(oldData, '2.0');
Sparse Data und Partial Updates
Sparse Objects
{
"user": {
"id": "user-123",
"name": "Max Mustermann",
"email": "max@example.com"
}
}
Partial Update (PATCH)
{
"op": "update",
"path": "/user/user-123",
"data": {
"email": "new-email@example.com"
}
}
JSON Patch (RFC 6902)
[
{
"op": "replace",
"path": "/user/email",
"value": "new-email@example.com"
},
{
"op": "add",
"path": "/user/phoneNumber",
"value": "+49 123 456789"
},
{
"op": "remove",
"path": "/user/temporaryField"
}
]
JSON Patch Implementation
import jsonpatch from 'fast-json-patch';
const document = {
user: {
name: "Max Mustermann",
email: "max@example.com"
}
};
const patches = [
{ op: "replace", path: "/user/email", value: "new@example.com" },
{ op: "add", path: "/user/phone", value: "+49 123 456" }
];
const updated = jsonpatch.applyPatch(document, patches).newDocument;
console.log(updated);
Metadaten und Envelope Pattern
Envelope Pattern
{
"meta": {
"requestId": "req-12345",
"timestamp": "2026-01-26T10:00:00Z",
"version": "1.0",
"pagination": {
"page": 1,
"pageSize": 20,
"total": 156,
"totalPages": 8
}
},
"data": [
{
"id": "item-1",
"name": "Item 1"
},
{
"id": "item-2",
"name": "Item 2"
}
],
"links": {
"self": "https://api.example.com/items?page=1",
"next": "https://api.example.com/items?page=2",
"prev": null,
"first": "https://api.example.com/items?page=1",
"last": "https://api.example.com/items?page=8"
}
}
HAL (Hypertext Application Language)
{
"_links": {
"self": { "href": "/orders/123" },
"customer": { "href": "/customers/456" },
"items": { "href": "/orders/123/items" }
},
"id": "123",
"total": 99.99,
"status": "pending",
"_embedded": {
"customer": {
"_links": {
"self": { "href": "/customers/456" }
},
"id": "456",
"name": "Max Mustermann"
}
}
}
Graph-Strukturen in JSON
Adjacency List
{
"graph": {
"nodes": [
{"id": "A", "label": "Node A"},
{"id": "B", "label": "Node B"},
{"id": "C", "label": "Node C"}
],
"edges": [
{"from": "A", "to": "B", "weight": 5},
{"from": "B", "to": "C", "weight": 3},
{"from": "A", "to": "C", "weight": 10}
]
}
}
Adjacency Matrix
{
"nodes": ["A", "B", "C"],
"adjacencyMatrix": [
[0, 5, 10],
[5, 0, 3],
[10, 3, 0]
]
}
Tree Structure
{
"tree": {
"id": "root",
"value": "Root Node",
"children": [
{
"id": "node-1",
"value": "Child 1",
"children": [
{
"id": "node-1-1",
"value": "Grandchild 1.1",
"children": []
}
]
},
{
"id": "node-2",
"value": "Child 2",
"children": []
}
]
}
}
Event Sourcing
Event Log
{
"events": [
{
"id": "evt-001",
"type": "UserCreated",
"timestamp": "2026-01-26T10:00:00Z",
"data": {
"userId": "user-123",
"email": "max@example.com",
"name": "Max Mustermann"
}
},
{
"id": "evt-002",
"type": "EmailUpdated",
"timestamp": "2026-01-26T10:05:00Z",
"data": {
"userId": "user-123",
"oldEmail": "max@example.com",
"newEmail": "new@example.com"
}
},
{
"id": "evt-003",
"type": "UserDeactivated",
"timestamp": "2026-01-26T10:10:00Z",
"data": {
"userId": "user-123",
"reason": "User request"
}
}
]
}
Event Replay
interface Event {
id: string;
type: string;
timestamp: string;
data: any;
}
class EventStore {
private events: Event[] = [];
append(event: Event) {
this.events.push(event);
}
replayEvents(initialState: any, handlers: Record<string, (state: any, event: Event) => any>): any {
return this.events.reduce((state, event) => {
const handler = handlers[event.type];
return handler ? handler(state, event) : state;
}, initialState);
}
}
// Verwendung
const store = new EventStore();
const handlers = {
UserCreated: (state, event) => ({
...state,
[event.data.userId]: {
id: event.data.userId,
email: event.data.email,
name: event.data.name,
active: true
}
}),
EmailUpdated: (state, event) => ({
...state,
[event.data.userId]: {
...state[event.data.userId],
email: event.data.newEmail
}
}),
UserDeactivated: (state, event) => ({
...state,
[event.data.userId]: {
...state[event.data.userId],
active: false
}
})
};
const currentState = store.replayEvents({}, handlers);
Best Practices
1. Konsistente Namenskonventionen
{
"camelCase": "JavaScript/TypeScript Standard",
"snake_case": "Python/Ruby Standard",
"PascalCase": "Typ-Namen",
"kebab-case": "URLs und Identifiers"
}
2. Vermeiden Sie über-verschachtelte Strukturen
// Schlecht: Über 5 Ebenen Verschachtelung
data.level1.level2.level3.level4.level5.value
// Besser: Normalisiert mit Referenzen
data.entities[id]
3. Verwenden Sie IDs für Beziehungen
{
"post": {
"id": "post-1",
"authorId": "user-1",
"categoryId": "cat-1"
}
}
4. Dokumentieren Sie Ihre Struktur
{
"$comment": "User profile structure v2.0",
"user": {
"id": "string (UUID)",
"email": "string (email format)",
"createdAt": "string (ISO 8601 timestamp)"
}
}
Zusammenfassung
Erweiterte JSON-Strukturen ermöglichen:
- Skalierbare Architekturen durch Normalisierung
- Flexibilität durch Polymorphismus
- Wartbarkeit durch Versionierung
- Performance durch effiziente Strukturierung
- Wiederverwendbarkeit durch Referenzen
Wählen Sie die Patterns basierend auf Ihren spezifischen Anforderungen und der Komplexität Ihrer Daten.
Verwandte Artikel
JSON Schema verstehen: Vollständiger Leitfaden zur Validierung
Lernen Sie JSON Schema: Definition, Validierung, Best Practices. Erstellen und verwenden Sie Schemas für robuste JSON-Datenvalidierung.
JSON in Data Science und Machine Learning
Erfahren Sie, wie JSON in Data Science, Machine Learning und KI verwendet wird. Praktischer Leitfaden für Datenaustausch, Modellspeicherung und APIs.
Arbeiten mit großen JSON-Dateien: Optimierung und Best Practices
Lernen Sie, wie man große JSON-Dateien effizient verarbeitet. Streaming, Parsing-Optimierung und Performance-Tipps für Big Data.