← Zurück zum Blog

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 Team16 Min. Lesezeitadvanced
B

Big JSON Team

Technical Writer

Expert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.

16 Min. Lesezeit

# 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.

Share:

Verwandte Artikel

Read in English